Generic lambda, inheritance and trailing return type: is this valid code?
Asked Answered
H

1

10

I was trying to reply to another answer and I found some difficulties while playing with lambdas and inheritance. Consider the following, minimal example:

template<typename Func>
struct Base: Func {
    Base(Func func): Func{func} {}

    template<typename... Args>
    auto operator()(Args... args)
    -> decltype(Func::operator()(args...), void()) {
        Func::operator()(args...);
    }
};

int main() {
    auto l = [](auto &&) {};
    Base<decltype(l)> mixin{l};
    mixin(0);
}

GCC 6.1 compiles it, clang 4.0 crashes (see https://godbolt.org/z/6Gz5s66h5). Note that both compile just fine using the following definition:

auto l = [](int) {};

Is this valid code or I'm doing something that is not allowed by the standard?


I've reported this as LLVM bug 31356, though I'd still like to know whether my code is correct.

Herringbone answered 12/12, 2016 at 23:19 Comment(10)
If a compiler crashes, it's a bug. Are there any other options?Tibetan
@n.m. Fair enough. I wanted to know if the code is valid actually. :-)Herringbone
May I suggest you edit the "is this a bug?" part out? I feel like it distracts from your actual question ("is this code valid").Mastin
@BaummitAugen Just done. Removed the word bug from the question.Herringbone
@n.m. if you compile an ill-formed program, crashing (with an ice message) meets the requirments of printing a diagnostic. Now so does print " \n", but I think a crash is a better QOI. ;)Jannet
Looks valid to me.Indecisive
@Yakk yes but it's still a bug.Tibetan
posted a new question as a follow-on #41117950Pleurisy
Still, congratulations for crashing Clang ;)Amargo
@Amargo I was trying to do it for a long time... :-DHerringbone
I
1

The bug report you've opened has been resolved in clang 14. Note that if a compiler crashes, this is always a bug, and not your fault.

Is this valid code or I'm doing something that is not allowed by the standard?

Yes, it is valid C++14 code, and more recent versions of clang allow it too. The only difference between [](auto &&) {}; and [](int) {} is that one lambda has a call operator that's a member function template, not just a member function.

That should not prevent you from inheriting from the lambda's closure type, or anything else you're doing in your code.

Workaround

The ICE seems to occur when parsing -> decltype(Func::operator()(args...), void()). You could instead write:

template<typename... Args>
auto operator()(Args... args) -> decltype(std::declval<Func&>()(args...), void())
{
    Func::operator()(args...);
}

See live example at Compiler Explorer using clang 13

Isle answered 28/8, 2023 at 20:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.