How to Use a Lambda Expression as a Template Parameter

Lambda expressions as class template parameters

As of C++20, this answer is now outdated. C++20 introduces stateless lambdas in unevaluated contexts1:

This restriction was originally designed to prevent lambdas from appearing in signatures, which would have opened a can of worm for mangling because lambdas are required to have unique types. However, the restriction is much stronger than it needs to be, and it is indeed possible to achieve the same effect without it

Some restrictions are still in place (e.g. lambdas still can't appear on function signatures), but the described usecase is now completely valid and the declaration of a variable is no longer necessary.



I'm asking if you can do something like:

Foo<decltype([]()->void { })> foo;

No you can't, because lambda expressions shall not appear in an unevaluated context (such as decltype and sizeof, amongst others).
C++0x FDIS, 5.1.2 [expr.prim.lambda] p2

The evaluation of a lambda-expression results in a prvalue temporary (12.2). This temporary is called the
closure object. A lambda-expression shall not appear in an unevaluated operand (Clause 5). [ Note: A
closure object behaves like a function object (20.8).—end note ]
(emphasis mine)

You would need to first create a specific lambda and then use decltype on that:

auto my_comp = [](const std::string& left, const std::string& right) -> bool {
// whatever
}

typedef std::unordered_map<
std::string,
std::string,
std::hash<std::string>,
decltype(my_comp)
> map_type;

That is because each lambda-derived closure object could have a completely different type, they're like anonymous functions after all.

How to use a lambda expression as a template parameter?

The 2nd template parameter of std::set expects a type, not an expression, so it is just you are using it wrongly.

You could create the set like this:

auto comp = [](const A& lhs, const A& rhs) -> bool { return lhs.x < rhs.x; };
auto SetOfA = std::set <A, decltype(comp)> (comp);

How I can pass lambda expression to c++ template as parameter

A lambda is not a function pointer! A lambda is an instance of compiler generated class!

However, a non capturing lambda may be converted to a function pointer using it's operator+

Here's an example:

int main() {
auto lambda = [](int a) { return a; };

func ptr = +lambda; // this would work

return 0;
}

Sadly, the operator+ won't even work in your case because it has not been declared as constexpr, so you can't use it in a template parameter.

A fix to your case would be to use a free function... until N4487 is not accepted, you can't expect to pass lambda as template parameter.

Another fix would be to create your own functor instead of a lambda:

struct LambdaType {
constexpr LambdaType() = default;

int operator()(int a) {
return run(a);
}

// this is a non-capturing lambda, the operator can be
// in a static function
static int run(int a) {
return a;
}
};

int main() {
LambdaType lambda;

function<&LambdaType::run>(1); // ---> this is working

return 0;
}

This solution is not quite appealing, but it might be useful if LambdaType is hidden in a cpp file.

If your goal is only the compiler to be able to inline your code, you can use templates to pass the lambda around:

#include <iostream>

template <typename T>
int function(T foo, int a) {
return foo(a);
}

int main() {
int a;
std::cin >> a;

int b = function([](int a) { return a; }, a);

return b;
}

Since the compiler knows the type of T for each instanciation, a good compiler should be able to optimize out the lambda.

With clang, the third option gives the following assembly:

main:                               # @main
pushq %rax
leaq 4(%rsp), %rsi
movl std::cin, %edi
callq std::basic_istream<char, std::char_traits<char> >::operator>>(int&)
movl 4(%rsp), %eax # this is the call to the function
addq $8, %rsp
retq

pushq %rax
movl std::__ioinit, %edi
callq std::ios_base::Init::Init()
movl std::ios_base::Init::~Init(), %edi
movl std::__ioinit, %esi
movl $__dso_handle, %edx
popq %rax
jmp __cxa_atexit # TAILCALL

I used -std=c++14 -Ofast -march=native as flags.

C++ Pass lambda to template parameter

Lambdas in C++14, including their conversion to function pointers, are not constexpr.

In C++17, this is going to change. There are no stable compilers with that feature implemented that I'm aware of (if you find one, can you mention it in the comments below?).

At that point

constexpr auto tmp = []() -> void { std::cout << "Hello world\n"; };
function<+tmp>();

will definitely work. I am uncertain if

function<+[]() -> void { std::cout << "Hello world\n"; }>()

would work; there are some rules about lambdas in unevaluated contexts and inside template argument lists that may be separate from the constexpr lambda problem and may apply here.

We can hack it in C++14.

Create a template class that stores a static copy of a lambda and exposes a static function with the same signature (f_ptr) that calls that static copy of a lambda.

Instantiate it once globally with your lambda.

Pass a pointer to the f_ptr to your template.

So:

template<class L> struct stateless; // todo
template<class L> stateless<L> make_stateless(L l){return std::move(l);}

auto foo = make_stateless( []() -> void { std::cout << "Hello world\n"; } );

function< &foo::f_ptr >();

this is almost certainly not what you want.

How to pass a lambda to a template paramater

In C++11? With a lambda as template parameter?

Given that a lambda (before C++20) can't be used in an unevaluated context (so in a decltype()) and can't be default constructed, I don't see a way.

The best I can imagine, in C++11, to reproduce something similar is something as follows

template <typename T, typename F = std::function<std::string(T const &)>>
std::string func (T const & t, F f = [](const T &t){return std::string{t};})
{ return f(t); }

that you can call with only the first argument

func("abc");

given that you accept the default lambda (but is saved as a std::function).

You can also pass another lambda, if you don't like the default one

func(1, [](int const &){ return "one"; });

but as a traditional argument, not as template parameter.

Lambda as template parameter

As the compiler is helpfully telling you, the problem is with this line:

return Order(val, other.val);

Since Order is a type (and not a function), that is calling Order's two-argument constructor. But it doesn't have one.

The correct syntax for invoking a functional class is:

return Order()(val, other.val);

However, that won't work either because the class generated for the lambda has a deleted default constructor.

In short, you need to instantiate your class with (the only) instance of the lambda.

Here's one possible way to proceed:

template<typename Order>
struct foo {
foo(Order compare) : compare_(compare) {}
bool operator<(const foo& other) {
return compare_(val, other.val);
}
int val;
Order compare_;
};

/* In practice, you'll want to template a parameter pack
* for the other arguments to the constructor, since those are likely.
* Also, you might want to use std::forward.
*/
template<typename Order>
foo<Order> foomaker(Order order) {
return foo<Order>(order);
}

int main() {
auto GoLess = [](int a,int b) -> bool
{
return a < b;
};

auto a = foomaker(GoLess);
auto b = foomaker(GoLess);
bool r = a < b;
return r;
}

Can a lambda instantiate a template function?

The answer to your question is technically yes, lambda bodies can instantiate template functions. The actual example doesn't work, because int n as a parameter can't be used that way.

There is an easy workaround

template<auto x>
using constant_t = std::integral_constant< std::decay_t<decltype(x)>, x >;
template<auto x>
constexpr constant_t<x> constant = {};

template <int n> int fn () { int arr[n] = {0}; return sizeof(arr); }
auto fx = [] (auto n) { return fn<n>(); };
std::cout << fx( constant<3> );

Live example.

Here I made the constant<x> variable template that creates an instance of std::integral_constant<X, x>. This is a stateless (but not valueless!) type that has a constexpr conversion to its value.

We can pass that to a lambda, and so long as the lambda takes it by value we can then convert it to a constexpr value within the lambda, including passing it as a template non-type parameter, instantiating a template function specialization as you are asking for.

The can be done without the constant variable template, ie if you don't have auto parameter support:

template<std::size_t N>
using index_t = std::integral_constant<std::size_t, N>;
template<std::size_t N>
constexpr index_t<N> index = {};

we can use a type-specific version of it, and just pass that, and it works the same way.


Aside, constant<?> is fun. For example:

using upFILE=std::unique_ptr<
std::FILE,
constant_t<std::fclose>
>;

upFILE file( fopen("hello.txt", "r") );

does the right thingtm.

Is it possible to explicitly specify template arguments in a generic lambda passed to a function?

In a generic lambda, operator() is a template, but the lambda type is not.

Instead of instantiating a template at an index F<I>{}(), one needs to instantiate operator() at an index. Since the lambda has captures, one will need to pass it instead of just the type as a template argument.

Replace:

template <template<std::size_t> typename F, std::size_t... I>
void iterator_implementation(
const std::index_sequence<I...> /*unused*/)
{
((F<I>{}()), ...);
}

with:

template <typename F, std::size_t... I>
void iterator_implementation(F f,
const std::index_sequence<I...> /*unused*/)
{

((f.template operator()<I>()), ...);
}

Full example: https://godbolt.org/z/rYTP1KTYc

How to invoke a lambda template?

The second definition is a variable template. It does not define the lambda's operator() as a template, but rather takes the parameter pack for the argument types of operator(). The resulting operator() is a regular member function of the instantiated variable's closure type. There is no template argument deduction possible here.

So when you write lamd<int>, the variable gets a closure type with a operator()(int), not something callable with 3 integers.

As mentioned already, you can use a generic lambda instead.

In C++20, if you'd need the lambda's argument types to be named and deduced, you can use the syntax:

auto lamd = []<typename... Pack>(Pack...) {}

This will define the operator as template, accepting a parameter pack, and leave the door open for template argument deduction.



Related Topics



Leave a reply



Submit