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

Ste*_*mer 87 lambda overloading c++11

在下面的代码中,第一次调用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)
}
Run Code Online (Sandbox Code Playgroud)

+这里的符号是什么?

dyp*_*dyp 90

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

一元运算+符的操作数应具有算术,无范围枚举或指针类型,结果是参数的值.

lambda不是算术类型等,但它可以转换:

[expr.prim.lambda]/3

的类型的λ-表达 [...]是一个独特的,无名不愈合类类型-称为闭合类型 -其特性如下所述.

[expr.prim.lambda]/6

用于闭合型λ-表达λ-捕获具有publicvirtualexplicit const转换功能函数指针具有相同的参数和返回类型为闭合类型的函数调用操作.此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果.

因此,一元+强制转换为函数指针类型,这是针对此lambda的void (*)().因此,表达式的类型+[](){}是此函数指针类型void (*)().

第二个重载void foo(void (*f)())在重载分辨率的排名中变为精确匹配,因此明确地选择(因为第一个重载不是精确匹配).


拉姆达[](){}可以转换为std::function<void()>通过的非显式模板构造函数std::function,它接受的任何类型的满足CallableCopyConstructible要求.

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

两者都是用户定义的转换序列,并且具有相同的等级.这就是为什么由于模糊性导致第一个例子中的重载决策失败的原因.


根据Cassio Neri的说法,在DanielKrügler的论证支持下,这种一元化的+技巧应该是指定的行为,即你可以依赖它(参见评论中的讨论).

如果你想避免歧义,我建议对函数指针类型使用显式强制转换:你不需要询问SO是什么以及为什么它起作用;)

  • VS15为您提供了这个有趣的错误:test.cpp(543):错误C2593:'operator +'不明确t\test.cpp(543):注意:可能是'内置C++运算符+(void(__ cdecl*)(void ))'t\test.cpp(543):注意:或'内置C++运算符+(void(__ stdcall*)(void))'t\test.cpp(543):注意:或'内置C++运算符+ (void(__ fastcall*)(void))'t\test.cpp(543):注意:或'内置C++运算符+(void(__ vectorcall*)(void))'t\test.cpp(543):注意:尝试匹配参数列表'(wmain :: <lambda_d983319760d11be517b3d48b95b3fe58>)test.cpp(543):错误C2088:'+':类非法 (5认同)
  • @Fred AFAIK成员函数指针无法转换为非成员函数指针,更不用说函数lvalues了.您可以通过`std :: bind`将成员函数绑定到`std :: function`对象,该对象可以与函数lvalue类似地调用. (3认同)
  • @DyP:没有`operator +()`的情况就是标准所描述的情况.该标准允许实现执行与指定内容不同的操作.例如,添加`operator +()`.但是,如果程序可以观察到这种差异,那么这是非法的.有一次我在comp.lang.c ++.moderated中询问,如果一个闭包类型可以为`result_type`添加一个typedef而另一个`typedefs`需要使它们适应(例如`std :: not1`).我被告知它不可能,因为这是可以观察到的.我会试着找到这个链接. (3认同)
  • @DyP:我相信我们可以依靠这个棘手的问题.实际上,假设一个实现将`operator +()`添加到无状态闭包类型.假设此运算符返回除闭包类型转换为的函数的指针之外的其他内容.然后,这将改变违反5.1.2/3的程序的可观察行为.如果您同意这个推理,请告诉我. (2认同)
  • @CassioNeri是的,这是我不确定的地方.我同意在添加`operator +`时可观察到的行为可能会改变,但这与没有`operator +`开头的情况相比较.但是没有指定闭包类型不应该有'operator +`重载."实现可以定义闭包类型与下面描述的不同,前提是这不会改变程序的可观察行为而不是[...]"但是IMO*添加*运算符不会将闭包类型更改为不同的从"下面描述的". (2认同)
  • @DyP:[here](https://groups.google.com/forum/?fromgroups#!searchin/comp.lang.c$2B$2B.moderated/adaptable$20closure$20type$20daniel$20cassio/comp.lang .c ++.moderated/6GM2DjHRMMc/xs5kb1nuSR4J)是链接. (2认同)