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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18889028/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:18:56  来源:igfitidea点击:

A positive lambda: '+[]{}' - What sorcery is this?

c++c++11lambdaoperator-overloadinglanguage-lawyer

提问by Daniel Frey

In Stack Overflow question Redefining lambdas not allowed in C++11, why?, a small program was given that does not compile:

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

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

The question was answered and all seemed fine. Then came Johannes Schauband made an interesting observation:

问题得到了回答,一切似乎都很好。然后来了Johannes Schaub并做了一个有趣的观察

If you put a +before the first lambda, it magically starts to work.

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

So I'm curious: Why does the following work?

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

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

It compiles fine with both GCC4.7+ and Clang3.2+. Is the code standard conforming?

它在GCC4.7+ 和Clang3.2+上都可以很好地编译。代码标准是否符合?

采纳答案by Daniel Frey

Yes, the code is standard conforming. The +triggers a conversion to a plain old function pointer for the lambda.

是的,代码符合标准。所述+触发转换为拉姆达一个普通的旧函数指针。

What happens is this:

发生的事情是这样的:

The compiler sees the first lambda ([]{}) and generates a closure object according to §5.1.2. As the lambda is a non-capturinglambda, the following applies:

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

5.1.2 Lambda expressions [expr.prim.lambda]

6The closure type for a lambda-expressionwith no lambda-capturehas 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.

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

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

This is important as the unary operator +has a set of built-in overloads, specifically this one:

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

13.6 Built-in operators [over.built]

8For every type Tthere exist candidate operator functions of the form

    T* operator+(T*);

13.6 内置操作符 [over.built]

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

    T* operator+(T*);

And with this, it's quite clear what happens: When operator +is applied to the closure object, the set of overloaded built-in candidates contains a conversion-to-any-pointer and the closure type contains exactly one candidate: The conversion to the function pointer of the lambda.

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

The type of testin auto test = +[]{};is therefore deduced to void(*)(). Now the second line is easy: For the second lambda/closure object, an assignment to the function pointer triggers the same conversion as in the first line. Even though the second lambda has a different closure type, the resulting function pointer is, of course, compatible and can be assigned.

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