Reputation: 2754
In C++17, is there a way to pass a "code block" (as a non-capturing lambda or function pointer) to a template parameter in an "inline" fashion without requiring extra lines to define it as a function?
I would like to use that, possibly wrapped in a macro, for defining an API for building a tree-like tuple-based structure.
Assuming
template <void (*FPtr) (int)>
struct Test {
static void test () {
FPtr (42);
}
};
as given, using it as
void test1 (int x) { std::cout << "test1(" << x << ")\n"; }
auto t1 = Test<test1> {};
works, but requires an extra line for the function definition. Using a lambda instead
constexpr auto test2 = static_cast<void (*) (int)> ([] (int x) { std::cout << "test2(" << x << ")\n"; });
auto t2 = Test<test2> {};
works too, but still requires an extra line. I'd like to inline the lambda as
auto t3 = Test<static_cast<void (*) (int)> ([] (int x) { std::cout << "test3(" << x << ")\n"; })> {};
but that results in an error in GCC (I need to use C++17):
lambda-expression in template-argument only available with '-std=c++20' or '-std=gnu++20'
Attempting to wrap both lines into yet another lambda
auto t4 = [] () {
static constexpr void (*fPTR) (int) = static_cast<void (*) (int)>( [] (int x) { std::cout << "test4(" << x << ")\n"; });
return Test<fPTR> {};
}
yields
<lambda()>::<lambda(int)>::_FUN' is not a valid template argument for type 'void (*)(int)' because 'static void<lambda()>::<lambda(int)>::_FUN(int)' has no linkage
Using a local class instead of a lambda
auto t5 = [] () {
struct X {
static void test (int x) {
std::cout << "test5(" << x << ")\n";
}
};
return Test<&X::test> {};
} ();
results in
<lambda()>::X::test' is not a valid template argument for type 'void (*)(int)' because 'static void<lambda()>::X::test(int)' has no linkage
Is there some other trick I'm not thinking of? I need to create my Test
instance using a single expression without additional declarations to provide an easy-to-use API.
I need it to work in GCC, Clang, and the IAR ARM Compiler (ICC based).
Upvotes: 1
Views: 717
Reputation: 2754
I just found out that it is possible to derive from lambdas. That means I can do something like:
template <typename F>
struct Test : F {
Test (F&& f) : F (std::move (f)) {}
};
and use as
Test ([] () { ... });
Since non-capturing lambda types are empty anyhow, Test
is an empty class so using it in an EBO-aware way e.g. using std::tuple
won't waste any memory.
Upvotes: 0
Reputation: 38287
As the compiler says, this is a C++20 feature, "lambdas in unevaluated contexts" (see P0315). gcc
has this since version 9, but it obviously requires -std=c++20
.clang
hasn't implemented this yet.
In pure C++17, there is no way to achieve this.
As a side note, you can force a conversion from a lambda to a function pointer by prefixing the lambda expression with a +
, as opposed to the more verbose static_cast
variant.
Upvotes: 2