Case 1:
Consider the following pack expansion in lambda noexcept
specifier:
template <bool... B>
auto g() {
([]() noexcept(B) {}, ...);
}
Clang and MSVC accept this code, but GCC rejects with:
error: expansion pattern '<lambda>' contains no parameter packs
Is this a valid code? Which Compiler should I trust?
Case 2:
Consider the following pack expansion in lambda requires-clause
:
template <bool... B>
auto g() {
([](auto) requires(B) {}, ...);
}
In this case, Clang and MSVC still accept this code, and GCC rejects it with the same error message. Is this just the same bug?
Case 3:
Consider the following pack expansion in the lambda template list:
template <typename... Args>
void g(Args...) {
([]<Args>(){}, ...);
}
This time three compiler all reject with the same error message:
expansion pattern '<lambda>' contains no parameter packs
Is there a difference compared to case 1? Or is this a common bug?
GCC fixed case 1 in 99584, and MSVC fixed case 3 in this.
Interesting exercise! Looking at the C++20 draft, the standard first distinguishes generic lambdas (7.5.5.5):
A lambda is a generic lambda if the lambda-expression has any generic parameter type placeholders (9.2.8.5), or if the lambda has a template-parameter-list.
int i = [](int i, auto a) { return i; }(3, 4); // OK: a generic lambda int j = []<class T>(T t, int i) { return i; }(3, 4); // OK: a generic lambda
Therefore, case 1 has non-generic lambdas and 2 and 3 have generic lambdas. It does not differentiate between type and non-type template parameters, so I think I agree with Yakk - Adam Nevraumont, all three should be admissible.
Lambdas are represented by an unnamed, unique closure type having a function call operator (operator()
) (template in case of a generic lambda), whose signature and constraints are determined by the lambda expression. The compiler could definitely satisfy each case with code resembling the following:
template<bool B>
struct closure {
auto operator()() const noexcept(B) {}
};
template <bool... B>
auto g() {
(closure<B>{}, ...);
}
template<bool B>
struct closure {
auto operator()(auto) const requires(B) {}
};
template <bool... B>
auto g() {
(closure<B>{}, ...);
}
template<typename T>
struct closure {
template<T>
auto operator()() const {}
};
template <typename... Args>
void g(Args...) {
(closure<Args>{}, ...);
}
These all compile on my machine, currently with gcc 11.2.1.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With