301

在堆栈溢出问题中重新定义 C++11 中不允许的 lambda,为什么?,给出了一个不能编译的小程序:

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

问题得到了回答,一切似乎都很好。然后约翰内斯·绍布( Johannes Schaub)做了一个有趣的观察

如果你+在第一个 lambda 之前加上 a,它就会神奇地开始工作。

所以我很好奇:为什么以下工作?

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

它可以与GCC 4.7+ 和Clang 3.2+ 一起编译。代码标准是否符合?

4

1 回答 1

278

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

会发生什么:

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

5.1.2 Lambda 表达式 [expr.prim.lambda]

6没有lambda-capture的lambda 表达式的闭包类型具有一个公共的非虚拟非显式 const 转换函数,该函数指向具有与闭包类型的函数调用运算符相同的参数和返回类型的函数的指针。这个转换函数的返回值应该是一个函数的地址,当被调用时,它与调用闭包类型的函数调用运算符具有相同的效果。

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

13.6 内置运算符 [over.built]

8对于每种类型T,都存在以下形式的候选运算符函数

    T* operator+(T*);

有了这个,很清楚会发生什么:当运算符+应用于闭包对象时,一组重载的内置候选对象包含一个转换为任意指针,而闭包类型恰好包含一个候选对象:转换为函数lambda 的指针。

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

于 2013-09-19T07:51:31.910 回答