使用 + (unary plus) 为 lambda 解决函数指针和 std::function 上的模棱两可的重载
Posted
技术标签:
【中文标题】使用 + (unary plus) 为 lambda 解决函数指针和 std::function 上的模棱两可的重载【英文标题】:Resolving ambiguous overload on function pointer and std::function for a lambda using + (unary plus) 【发布时间】:2013-07-23 05:27:56 【问题描述】:在以下代码中,对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)
+
符号在这里做什么?
【问题讨论】:
【参考方案1】:表达式+[]()
中的+
是一元+
运算符。定义如下
[expr.unary.op]/7:
一元
+
运算符的操作数应具有算术、无范围枚举或指针类型,结果是参数的值。
lambda 不是算术类型等,但可以转换:
[expr.prim.lambda]/3
lambda-expression [...] 的类型是唯一的、未命名的非联合类类型——称为 闭包类型——其属性如下所述.
[expr.prim.lambda]/6
没有 lambda-capture 的 lambda-expression 的闭包类型具有
public
non-virtual
non-explicit
const
转换函数指向函数的指针 具有与闭包类型的函数调用运算符相同的参数和返回类型。这个转换函数的返回值应该是一个函数的地址,当被调用时,它与调用闭包类型的函数调用运算符具有相同的效果。
因此,一元 +
强制转换为函数指针类型,即针对此 lambda void (*)()
。因此,表达式+[]()
的类型就是这个函数指针类型void (*)()
。
第二个重载void foo(void (*f)())
成为重载解析排名中的精确匹配,因此被明确选择(因为第一个重载不是精确匹配)。
lambda []()
可以通过std::function
的非显式模板ctor 转换为std::function<void()>
,它采用满足Callable
和CopyConstructible
要求的任何类型。
也可以通过闭包类型的转换函数将lambda转换为void (*)()
(见上文)。
两者都是用户定义的转换序列,并且等级相同。这就是为什么在 first 示例中由于歧义而导致重载解析失败的原因。
根据由 Daniel Krügler 的论点支持的 Cassio Neri 所说,这个一元 +
技巧应该是指定的行为,即您可以依赖它(参见 cmets 中的讨论)。
不过,如果您想避免歧义,我还是建议您对函数指针类型使用显式转换:您无需询问 SO 是做什么的以及为什么起作用;)
【讨论】:
@Fred AFAIK 成员函数指针不能转换为非成员函数指针,更不用说函数左值了。您可以通过std::bind
将成员函数绑定到std::function
对象,该对象可以像函数左值一样被调用。
@DyP:我相信我们可以依靠棘手的。实际上,假设实现将operator +()
添加到无状态闭包类型。假设此运算符返回的不是指向闭包类型转换为的函数的指针。然后,这将改变违反 5.1.2/3 的程序的可观察行为。请告诉我你是否同意这个推理。
@CassioNeri 是的,这就是我不确定的地方。我同意添加operator +
时可观察到的行为可能会发生变化,但这与没有operator +
开始的情况相比。但是没有指定闭包类型不应有operator +
重载。 “一个实现可以定义不同于下面描述的闭包类型,前提是这不会改变程序的可观察行为,而不是通过 [...]”但是 IMO 添加运算符不会改变闭包类型不同于“下面描述的”。
@DyP:没有operator +()
的情况正是标准所描述的情况。该标准允许实现做一些不同于指定的事情。例如,添加operator +()
。但是,如果程序可以观察到这种差异,那么它就是非法的。一旦我在 comp.lang.c++.moderated 中询问一个闭包类型是否可以为result_type
和另一个typedefs
添加一个typedef 以使其具有适应性(例如std::not1
)。我被告知它不能,因为这是可以观察到的。我会尝试找到链接。
VS15 给你这个有趣的错误:test.cpp(543): error C2593: 'operator +' is ambiguous t\test.cpp(543): note: could be 'built-in C++ operator+ (void (__cdecl *)(void))' t\test.cpp(543): note: or 'built-in C++ operator+(void (__stdcall *)(void))' t\test.cpp(543): note : or 'built-in C++ operator+(void (__fastcall *)(void))' t\test.cpp(543): note: or 'built-in C++ operator+(void (__vectorcall *)(void))' t\ test.cpp(543): 注意: 在尝试匹配参数列表时'(wmain::以上是关于使用 + (unary plus) 为 lambda 解决函数指针和 std::function 上的模棱两可的重载的主要内容,如果未能解决你的问题,请参考以下文章
SpringBoot整合MyBatis-Plus3.1详细教程