在 Stack Overflow 问题 Redefining lambdas not allowed in C++11, why? ,给出了一个无法编译的小程序:
int main() {
auto test = []{};
test = []{};
}
问题已得到解答,一切似乎都很好。然后来了Johannes Schaub并制作了an interesting observation :
If you put a +
before the first lambda, it magically starts to work.
所以我很好奇:为什么下面的方法有效?
int main() {
auto test = +[]{}; // Note the unary operator + before the lambda
test = []{};
}
GCC 都能很好地编译4.7+ 和 Clang 3.2+。代码标准是否符合?
是的,代码符合标准。 +
触发转换为 lambda 的普通旧函数指针。
这是怎么回事:
编译器看到第一个 lambda ([]{}
) 并根据 §5.1.2 生成一个闭包对象。由于 lambda 是一个非捕获 lambda,因此适用以下条件:
5.1.2 Lambda expressions [expr.prim.lambda]
6 The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.
这很重要,因为一元运算符 +
有一组内置的重载,特别是这个:
13.6 Built-in operators [over.built]
8 For every type T
there exist candidate operator functions of the form
T* operator+(T*);
有了这个,很清楚会发生什么:当运算符 +
应用于闭包对象时,重载的内置候选集包含一个转换为任意指针和闭包类型只包含一个候选者:到 lambda 函数指针的转换。
auto test = +[]{};
中的test
类型因此被推导为 void(*)()
。现在第二行很简单:对于第二个 lambda/闭包对象,对函数指针的赋值触发与第一行相同的转换。即使第二个 lambda 具有不同的闭包类型,生成的函数指针当然是兼容的并且可以赋值。