106

在下面的代码中,第一次调用foo是不明确的,因此无法编译。

第二个,+在 lambda 之前添加,解析为函数指针重载。

#include <functional>

void foo(std::function<void()> f) { f(); }
void foo(void (*f)()) { f(); }

int main ()
{
    foo(  [](){} ); // ambiguous
    foo( +[](){} ); // not ambiguous (calls the function pointer overload)
}

+符号在这里做什么?

4

1 回答 1

110

+表达式中的+[](){}是一元运算+符。在[expr.unary.op]/7中定义如下:

一元运算+符的操作数应具有算术、无范围枚举或指针类型,结果是参数的值。

lambda 不是算术类型等,但可以转换:

[expr.prim.lambda]/3

lambda-expression [...]的类型是唯一的、未命名的非联合类类型——称为闭包类型——其属性如下所述。

[expr.prim.lambda]/6

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

因此,一元+强制转换为用于此 lambda 的函数指针类型void (*)()。因此,表达式的类型+[](){}就是这个函数指针类型void (*)()

第二个重载void foo(void (*f)())成为重载解决排名中的精确匹配,因此被明确选择(因为第一个重载不是精确匹配)。


可以通过 的非显式模板 ctor将 lambda[](){}转换为,它采用满足和要求的任何类型。std::function<void()>std::functionCallableCopyConstructible

lambda 也可以通过闭包类型void (*)()的转换函数转换为(见上文)。

两者都是用户定义的转换序列,并且等级相同。这就是为什么在第一个示例中由于歧义而导致重载解析失败的原因。


根据由 Daniel Krügler 支持的 Cassio Neri 的说法,这个一元+技巧应该是指定的行为,即您可以依赖它(参见评论中的讨论)。

不过,如果您想避免歧义,我建议您对函数指针类型使用显式强制转换:您无需询问 SO 是做什么的以及它为什么起作用;)

于 2013-07-23T22:41:02.063 回答