为什么在模板实例化中不允许使用lambda?

时间:2018-12-03 04:22:36

标签: c++ templates lambda language-lawyer c++17

我目前正在处理一些代码,其中需要使用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出现在方法签名中,而没有任何关于为什么这样做有问题的信息。

1 个答案:

答案 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提出的这种限制避免了模板参数推导方面的严重实现困难(例如,这避免了需要用乱码来编码任意语句序列)。