使用+解决lambda函数指针和std :: function的模糊过载

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

第二,在lambda之前加上+ ,parsing为函数指针重载。

 #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) } 

在这里做的记号是什么?

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

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

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

[expr.prim.lambda] / 3

lambdaexpression式 […]的types是一个唯一的,未命名的非联合类types – 称为闭包types – 其属性在下面描述。

[expr.prim.lambda] / 6

不带lambda捕获lambdaexpression式的闭包types具有publicvirtualexplicit const转换函数, 指向具有与闭包types的函数调用运算符相同的参数和返回types的函数。 这个转换函数返回的值应该是被调用的函数的地址,和调用闭包types的函数调用操作符一样。

因此,一元+强制转换为函数指针types,这是为这个lambda void (*)() 。 因此,expression式+[](){}types是这个函数指针typesvoid (*)()

第二个重载void foo(void (*f)())在重载分辨率排名中成为一个精确匹配,因此被明确select(因为第一个重载不是精确匹配)。


lambda [](){}可以通过std::function<void()>的非显式模板ctor转换为std::function<void()> ,该模板采用满足CallableCopyConstructible需求的任何types。

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

两者都是用户定义的转换序列,并且具有相同的等级。 这就是为什么在第一个例子中由于含糊不清而导致重载parsing失败的原因。


根据Cassio Neri的说法,DanielKrügler提出的一个论点支持,这个一元+技巧应该是指定的行为,也就是说,你可以依靠它(参见评论中的讨论)。

不过,如果你想避免模糊性,我build议使用显式types转换为函数指针types:你不需要问什么是做什么,为什么它工作;)