Somehow I still think of lambdas as “syntactic sugar” for regular function objects, so it surprises me that under C++-20 a stateful but otherwise constexpr
lambda instance cannot be used as a non-type template parameter, unlike an equivalent function object instance.
Can anyone explain this behavior or decision?
Example on godbolt:
struct always_fn {
const int x;
int operator()() const
{
return x;
}
};
inline constexpr always_fn always_f{5};
// lambda equivalent to `always_f`
inline constexpr auto always_2_f = [x = 5]() {
return x;
};
template<typename F>
struct wrapped {
F f_;
};
inline constexpr auto wrapped_f = wrapped{always_f};
inline constexpr auto wrapped_2_f = wrapped{always_2_f};
template<auto f>
void pass() {}
int main() {
pass<always_f>();
pass<wrapped_f>();
// error: no matching function for call to 'pass'
pass<always_2_f>();
// ^~~~~~~~~~~~~~~~
// error: no matching function for call to 'pass'
pass<wrapped_2_f>();
// ^~~~~~~~~~~~~~~~~
return 0;
}
Lambdas (whether stateful or not) are never structural types and therefore can never be used as non-type template argument.
This was clarified by CWG 2542 which compilers might not implement yet.
Without making such a decision, whether or not the lambda has a structural type would depend on implementation details such as whether the members of the closure type are private/public or the implementation of the closure type would have to be specified in much more detail.
In particular, there is nothing requiring that x
in your equivalent function object should be public and if it isn’t, then it wouldn’t be usable as non-type template argument either.
For the record, GCC accepts this code, while Clang and MSVC reject it.