Does C++20 allow a non-capturing lambda decayed to a function pointer to be passed directly as a non-type template parameter? If so, what is the correct syntax?
I have tried the following code in various versions of clang and gcc using -std=c++2a
.
#include <iostream>
template<auto f>
struct S {
static void invoke(int x) { f(x); }
};
using X = S<+[](int x) -> void { std::cout << x << " hello\n"; }>;
int main()
{
X::invoke(42);
}
gcc compiles the code without complaint and the code runs as expected.
clang fails compilation with the following error:
error: a lambda expression cannot appear in this context
using X = S<+[](int x) -> void { std::cout << x << " hello\n"; }>;
^
Here is the full code (online versions):
Clang 10.0.0 HEAD: https://wandbox.org/permlink/n5eKQ4kQqSpDpr4k
Gcc 10.0.0 HEAD 20200113: https://wandbox.org/permlink/vJ44sdMtwCKAFU64
Does C++20 allow a non-capturing lambda decayed to a function pointer to be passed directly as a non-type template parameter?
Yes.
Indeed, you can go one step further - you don't even need to convert the lambda to a function pointer. You can just provide the lambda. This is valid C++20:
using Y = S<[](int x) -> void { std::cout << x << " hello\n"; }>;
Sorry this is no longer considered valid C++20, since CWG 2542, lambdas are not "structural" types (the term for which types are usable as non-type template parameter types).
The rule we have in C++20 is that lambdas are now allowed in unevaluated contexts (P0315). Among many other wording changes there, this paper struck the rule that prevented lambdas from being used in template arguments (C++17's [expr.prim.lambda]/2):
A lambda-expression shall not appear in an unevaluated operand, in a template-argument, in an alias-declaration, in a typedef declaration, or in the declaration of a function or function template outside its function body and default arguments.
That clause does not exist anymore in C++20.
Removing this restriction allows the lambda to be used as a template argument, and the conversion from captureless lambda to function pointer was already constexpr in C++17. clang simply does not implement this feature yet (using T = decltype([]{});
compiles on gcc, not yet on clang). I wouldn't call this a clang bug yet, it's just a clang not-yet-implemented-feature (lambdas in unevaluated contexts is not yet listed as implemented in the cppreference compiler support page).
C++20 non-type template parameters (P1907) allows even dropping the +
because captureless lambdas count as structural types ([temp.param]/7) by way of simply not having any data members at all.