正lambda: '+[]{}'-这是什么魔法?

在Stack Overflow问题在c++ 11中不允许重新定义lambdas,为什么?< / >中,给出了一个不能编译的小程序:

int main() {
auto test = []{};
test = []{};
}

问题得到了回答,一切似乎都很好。然后是约翰内斯他并生成一个有趣的观察:

如果你把+放在第一个lambda之前,它就神奇地开始工作了。

所以我很好奇:为什么下面的方法有效?

int main() {
auto test = +[]{}; // Note the unary operator + before the lambda
test = []{};
}

它可以在海湾合作委员会 4.7+和铿锵声 3.2+下编译。代码符合标准吗?

35942 次浏览

是的,代码符合标准。+触发lambda到普通旧函数指针的转换。

结果是这样的:

编译器看到第一个lambda ([]{})并根据§5.1.2生成一个闭包对象。由于lambda是 lambda,因此适用于:

5.1.2 Lambda表达式[exp .prim. Lambda]

6不带lambda-capturelambda表达式的闭包类型有一个公共非虚拟非显式const转换函数,该函数指向与闭包类型的函数调用操作符具有相同的形参和返回类型的函数。此转换函数返回的值应该是函数的地址,该函数在调用时与调用闭包类型的函数调用操作符具有相同的效果。

这很重要,因为一元操作符+有一组内置重载,特别是这个:

13.6内置操作符[over.built]

对于每一种类型T,都存在这样形式的候选操作符函数

T* operator+(T*);

有了这个,发生的事情就很清楚了:当运算符+应用于闭包对象时,重载的内置候选集包含一个转换到任意指针的候选者,闭包类型只包含一个候选者:lambda的函数指针的转换。

因此,auto test = +[]{};中的test类型被推导为void(*)()。现在第二行很简单:对于第二个lambda/closure对象,对函数指针的赋值触发与第一行相同的转换。尽管第二个lambda具有不同的闭包类型,但生成的函数指针当然是兼容的,并且可以被赋值。