+
表达式中的+[](){}
是一元运算+
符。在[expr.unary.op]/7中定义如下:
一元运算+
符的操作数应具有算术、无范围枚举或指针类型,结果是参数的值。
lambda 不是算术类型等,但可以转换:
[expr.prim.lambda]/3
lambda-expression [...]的类型是唯一的、未命名的非联合类类型——称为闭包类型——其属性如下所述。
[expr.prim.lambda]/6
没有lambda-capture的lambda 表达式的闭包类型有一个非非转换函数,指向函数的指针,该函数具有与闭包类型的函数调用运算符相同的参数和返回类型。这个转换函数返回的值应该是一个函数的地址,当被调用时,它与调用闭包类型的函数调用运算符具有相同的效果。public
virtual
explicit
const
因此,一元+
强制转换为用于此 lambda 的函数指针类型void (*)()
。因此,表达式的类型+[](){}
就是这个函数指针类型void (*)()
。
第二个重载void foo(void (*f)())
成为重载解决排名中的精确匹配,因此被明确选择(因为第一个重载不是精确匹配)。
可以通过 的非显式模板 ctor将 lambda[](){}
转换为,它采用满足和要求的任何类型。std::function<void()>
std::function
Callable
CopyConstructible
lambda 也可以通过闭包类型void (*)()
的转换函数转换为(见上文)。
两者都是用户定义的转换序列,并且等级相同。这就是为什么在第一个示例中由于歧义而导致重载解析失败的原因。
根据由 Daniel Krügler 支持的 Cassio Neri 的说法,这个一元+
技巧应该是指定的行为,即您可以依赖它(参见评论中的讨论)。
不过,如果您想避免歧义,我建议您对函数指针类型使用显式强制转换:您无需询问 SO 是做什么的以及它为什么起作用;)