我目前正在处理一些代码,其中需要使用lambda作为模板参数,如以下示例所示:
#include <iostream>
template<void(*func)(int)>
struct FunctionCaller{
FunctionCaller() {func(5);}
};
void RealFunction(int i){
std::cout << i*2 << std::endl;
}
int main(){
FunctionCaller<[](int i){std::cout << i << std::endl;}> caller; //should print 5
FunctionCaller<RealFunction> caller2; //should print 10
return 0;
}
我尝试使用GCC和Clang的最新版本(分别支持C ++ 17)和Clang(分别为8.2.1和7.0.0版本)编译此示例,以获取各种错误,这些错误等同于模板参数中不允许的lambda表达式。我正在使用正确的编译器标志来启用C ++ 17支持。
但是,在我看来,这似乎应该可以使用,因为C ++ Primer 5th Edition指出,任何常量表达式都可以用于非类型模板参数,并且在C ++ 17中,并且这篇Stack Overflow帖子告诉我lambda是C ++ 17及更高版本中的常量表达式。
经过大量研究,似乎C ++ 17标准明确禁止使用lambda作为模板参数。此限制的依据是什么?如果没有安装,会炸毁什么?
编辑:This问题具有一些相关信息,但仅表示存在此限制是为了防止lambda出现在方法签名中,而没有任何关于为什么这样做有问题的信息。
答案 0 :(得分:4)
经过一番搜索,我发现引入了此限制的更改:core issue 1607。它说:
关于功能模板签名中的 lambda-表达式的进一步讨论。尽管限制 lambda-expressions 不能作为未评估的操作数出现(8.1.5 [expr.prim.lambda]第2段)是为了避免在功能模板签名中处理它们的需要, 8.20 [expr.const]将未评估的子表达式与未评估的操作数分开对待,为模板签名中的 lambda-表达式开辟了另一条途径,例如
template<typename T> void f(int [(0 && [] { for (auto x : T()) {} }, 1)]);
Core给了EWG 4个选项,他们选择了C ++ 17和以前的版本:禁止在签名中使用lambda-模板参数等等。
现在为什么lambda不能出现在功能模板签名中? N2903中对此进行了解释:
此外,此重写添加了以下限制:不能在
sizeof
运算符,alignof
运算符或decltype
指定符的操作数中使用lambda表达式。 Doug Gregor和John Spicer提出的这种限制避免了模板参数推导方面的严重实现困难(例如,这避免了需要用乱码来编码任意语句序列)。