Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda expressions as class template parameters in C++14

The question Lambda expressions as class template parameters asks about the possibility of using lambda expressions as class template parameters.

The answer to the question was no. However, it was about C++11.

Has the situation changed in the new standard, C++14?

like image 262
DLunin Avatar asked Oct 02 '14 19:10

DLunin


2 Answers

No the situation in C++14 has not changed at all and in fact the language in section 5.1.2 Lambda expressions paragraph 2 has been tightened from:

A lambda-expression shall not appear in an unevaluated operand (Clause 5).

to:

[...]A lambda-expression shall not appear in an unevaluated operand (Clause 5), in a templateargument, 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. [ Note: The intention is to prevent lambdas from appearing in a signature. —end note ][...]

Defect report 1607. Lambdas in template parameters lead to this change.

The defect report only obliquely deals with the rationale for disallowing this but we can find a very detailed explanation for why this is disallowed in Rationale for lambda-expressions not being allowed in unevaluated contexts. The reasons boil down to:

  • Lambda expressions not having a unique type
  • Compiler implementation issues:
    • Such as an extraordinary expansion of SFINAE
    • The possible requirement to name mangle the whole body of a lambda.

Given the rationale for this restriction it seems unlikely to change.

like image 80
Shafik Yaghmour Avatar answered Sep 26 '22 10:09

Shafik Yaghmour


Has the situation changed in the new standard, C++14?

A Lambda expression shall still not appear in an unevaluated operand - the same quote as the one in Xeo's Post also exists in the latest publicly available draft N3797, in the exact same place.

However, every closure type has a deleted default constructor (N3797, §5.1.2/20):

The closure type associated with a lambda-expression has a deleted (8.4.3) default constructor and a deleted copy assignment operator.

So, for portabiliby and standard conformance (and probably for the code to work on reasonable compilers), you would need to pass a closure object to the constructor of the instantiated class to copy from. But to pass a closure object of the same type as the template argument of that specialization you have to define it first anyway:

using my_map_type = map<int, int, decltype([] (auto&& lhs, auto&& rhs) {return lhs < rhs*4;})>;
// Assuming the above compiles

my_map_type m( [] (auto&& lhs, auto&& rhs) {return lhs < rhs*4;} );
// Different closure type - compiler error! What do you copy from!? 

There isn't any legal way to create a single object of the closure type of the first lambda. Therefore, even if said rule was to be removed, you couldn't create a single instance of my_map_type. Similar problems occur with other "closure type as template argument" scenarios.

like image 31
Columbo Avatar answered Sep 26 '22 10:09

Columbo