使用+解决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具有
public
非virtual
非explicit
const
转换函数, 指向具有与闭包types的函数调用运算符相同的参数和返回types的函数。 这个转换函数返回的值应该是被调用的函数的地址,和调用闭包types的函数调用操作符一样。
因此,一元+
强制转换为函数指针types,这是为这个lambda void (*)()
。 因此,expression式+[](){}
types是这个函数指针typesvoid (*)()
。
第二个重载void foo(void (*f)())
在重载分辨率排名中成为一个精确匹配,因此被明确select(因为第一个重载不是精确匹配)。
lambda [](){}
可以通过std::function<void()>
的非显式模板ctor转换为std::function<void()>
,该模板采用满足Callable
和CopyConstructible
需求的任何types。
lambda也可以通过闭包types的转换函数转换为void (*)()
(见上文)。
两者都是用户定义的转换序列,并且具有相同的等级。 这就是为什么在第一个例子中由于含糊不清而导致重载parsing失败的原因。
根据Cassio Neri的说法,DanielKrügler提出的一个论点支持,这个一元+
技巧应该是指定的行为,也就是说,你可以依靠它(参见评论中的讨论)。
不过,如果你想避免模糊性,我build议使用显式types转换为函数指针types:你不需要问什么是做什么,为什么它工作;)