在下面的代码中,第一次调用
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)
}
这里的
+
符号是做什么用的?
表达式
+
中的 +[](){}
是一元 +
运算符。它的定义如下
[expr.unary.op]/7:
一元
运算符的操作数应为算术、无作用域枚举或指针类型,结果为参数的值。+
lambda 不是算术类型等,但可以转换:
[expr.prim.lambda]/3
[expr.prim.lambda]/6lambda 表达式 [...] 的类型是一种独特的、未命名的非联合类类型 — 称为 闭包类型 — 其属性如下所述。
不带因此,一元lambda-capture 的 lambda-expression 的闭包类型具有
public
非virtual
非explicit
const
到函数指针 的转换函数,具有相同的参数和返回值类型作为闭包类型的函数调用运算符。此转换函数返回的值应是函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果。
+
强制转换为函数指针类型,也就是针对这个lambda
void (*)()
。因此,表达式
+[](){}
的类型就是这个函数指针类型
void (*)()
。第二个重载
void foo(void (*f)())
成为重载解析排名中的精确匹配,因此被明确选择(因为第一个重载不是精确匹配)。
[](){}
可以通过
std::function<void()>
的非显式模板构造函数转换为
std::function
,它采用满足
Callable
和
CopyConstructible
要求的任何类型。lambda 还可以通过
闭包类型
的转换函数转换为
void (*)()
(见上文)。
两者都是用户定义的转换序列,并且具有相同的等级。这就是为什么重载决策在 first 示例中由于不明确而失败的原因。
+
技巧应该是指定的行为,即你可以依赖它(请参阅评论中的讨论)。不过,如果您想避免歧义,我建议您使用显式转换为函数指针类型:您不需要询问它的作用和原因;)