Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda expressions as class template parameters

Can lambda expressions be used as class template parameters? (Note this is a very different question than this one, which asks if a lambda expression itself can be templated.)

I'm asking if you can do something like:

template <class Functor>  struct Foo { }; // ... Foo<decltype([]()->void { })> foo; 

This would be useful in cases where, for example, a class template has various parameters like equal_to or something, which are usually implemented as one-liner functors. For example, suppose I want to instantiate a hash table which uses my own custom equality comparison function. I'd like to be able to say something like:

typedef std::unordered_map<   std::string,   std::string,   std::hash<std::string>,   decltype([](const std::string& s1, const std::string& s2)->bool      { /* Custom implementation of equal_to */ })   > map_type; 

But I tested this on GCC 4.4 and 4.6, and it doesn't work, apparently because the anonymous type created by a lambda expression doesn't have a default constructor. (I recall a similar issue with boost::bind.) Is there some reason the draft standard doesn't allow this, or am I wrong and it is allowed but GCC is just behind in their implementation?

like image 988
Channel72 Avatar asked May 01 '11 14:05

Channel72


People also ask

Can lambda be templated?

From the various lambda improvements, template parameters for lambdas are my favorite ones. Lambdas support with C++20 template parameters, can be default-constructed and support copy-assignment, when they have no state, and can be used in unevaluated contexts.

How do you pass a lambda expression as a method parameter in Java?

Passing Lambda Expressions as Arguments If you pass an integer as an argument to a function, you must have an int or Integer parameter. If you are passing an instance of a class as a parameter, you must specify the class name or the object class as a parameter to hold the object.

Can lambda be Constexpr?

Visual Studio 2017 version 15.3 and later (available in /std:c++17 mode and later): A lambda expression may be declared as constexpr or used in a constant expression when the initialization of each data member that it captures or introduces is allowed within a constant expression.

How do you create a method which takes a lambda expression as a parameter?

Basically to pass a lamda expression as a parameter, we need a type in which we can hold it. Just as an integer value we hold in primitive int or Integer class. Java doesn't have a separate type for lamda expression instead it uses an interface as the type to hold the argument.


1 Answers

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.

like image 58
Xeo Avatar answered Sep 21 '22 15:09

Xeo