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