Lambda表达式作为C++14中的类模板参数

Lambda expressions as class template parameters in C++14

本文关键字:参数 表达式 C++14 Lambda      更新时间:2023-10-16

Lambda表达式作为类模板参数的问题询问了使用Lambda表达式作为类样板参数的可能性。

这个问题的答案是否定的。然而,它是关于C++11的。

新标准C++14中的情况发生了变化吗?

不,C++14中的情况根本没有改变,事实上,5.1.2Lambda表达式段落2中的语言已经从收紧

lambda表达式不应出现在未赋值的操作数中(子句5) 。

至:

[…]lambda表达式不应出现在未赋值的操作数中(第5条),在模板中,在别名声明中,在typedef声明,或者在函数或函数的声明中模板在其函数体和默认参数之外。[注:目的是防止lambdas出现在签名中--终止注][…]

缺陷报告1607。模板参数中的Lambdas导致了此更改。

缺陷报告只是间接地处理了不允许这样做的理由,但我们可以找到一个非常详细的解释,解释为什么在未评估的上下文中不允许lambda表达式的理由中不允许这样。原因可以归结为:

  • Lambda表达式没有唯一类型
  • 编译器实现问题:
    • 比如SFINAE的非凡扩展
    • 对lambda的整个主体进行命名的可能要求

考虑到这一限制的理由,它似乎不太可能改变。

新标准C++14中的情况发生了变化吗?

Lambda表达式仍然不能出现在未赋值的操作数中——与Xeo的Post中的引号相同的引号也存在于最新公开的草案N3797中,位置完全相同。

然而,每个闭包类型都有一个已删除的默认构造函数(N3797,§5.1.2/20):

lambda表达式关联的闭包类型已删除(8.4.3)默认构造函数和已删除的副本赋值运算符。

因此,为了实现可移植性和标准一致性(可能还有代码在合理的编译器上工作),您需要将闭包对象传递给实例化类的构造函数以进行复制。但是要传递与该专门化的模板参数类型相同的闭包对象,无论如何都必须首先定义它:

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!? 

没有任何合法的方法来创建第一个lambda的闭包类型的单个对象。因此,即使要删除上述规则,也不能创建my_map_type的单个实例。类似的问题也出现在其他"闭包类型作为模板参数"的场景中。