使用 +(一元加号)解决 lambda 的函数指针和 std::function 上的不明确重载
Resolving ambiguous overload on function pointer and std::function for a lambda using + (unary plus)
在下面的代码中,对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)
}
+
符号在这里做什么?
表达式 +[](){}
中的+
是一元+
运算符。它的定义如下[expr.unary.op]/7:
一元运算符的操作数
+
应具有算术、无作用域枚举或指针类型,结果是参数的值。
lambda 不是算术类型等,但可以转换:
[expr.prim.lambda]/3
lambda-expression [...] 的类型是一个唯一的、未命名的非联合类类型——称为闭包类型——其属性如下所述。
[expr.prim.lambda]/6
没有 lambda 捕获的 lambda 表达式的闭包类型具有
public
非virtual
非explicit
const
函数到指向函数的指针,该函数具有与闭包类型的函数调用运算符相同的参数和返回类型。此转换函数返回的值应是函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果。
因此,一元+
强制转换为函数指针类型,该类型适用于此 lambda void (*)()
。因此,表达式+[](){}
的类型是此函数指针类型 void (*)()
。
第二个重载void foo(void (*f)())
成为重载分辨率排名中的完全匹配,因此被明确选择(因为第一个重载不是完全匹配(。
lambda [](){}
可以通过 std::function
的非显式模板 ctor 转换为 std::function<void()>
,它采用满足Callable
和CopyConstructible
要求的任何类型。
lambda 也可以通过闭包类型的转换函数转换为 void (*)()
(见上文(。
两者都是用户定义的转换序列,并且具有相同的等级。这就是重载解析在第一个示例中由于歧义而失败的原因。
根据卡西奥·内里(Cassio Neri(的说法,在丹尼尔·克鲁格勒(Daniel Krügler(的论点的支持下,这种一元+
技巧应该是特定的行为,即你可以依赖它(见评论中的讨论(。
不过,如果你想避免歧义,我还是建议对函数指针类型使用显式强制转换:你不需要问SO它的作用和为什么它;)
- C++指针复制重载
- 如何在基类指针向量的元素上应用重载的多态函数
- 从纯虚拟类 (A) 派生的指针无法访问来自纯类 (B) 的重载方法
- 错误:表达式必须具有算术、无作用域枚举或带有运算符重载的指针类型
- 指向重载静态成员的函数指针 - 在unique_ptr中用作自定义删除器
- 重载 ostream << 运算符,指针作为参数,导致输出上的内存地址
- 使用类指针重载C++命名空间函数模板专用化替代方法?
- 类重载运算符 '<' 插入指向该对象集的共享指针时不调用
- 使用继承的指针列表复制构造函数或重载运算符=
- 为指针重载运算符++
- 如何从模板类重载创建的指针对象上的运算符?
- 用指针重载运算符+
- 函数指针重载函数
- 函数指针重载错误
- 矢量可以包含一个智能指针,该指针重载运算符&?
- 模板函数指针重载
- C++:使用函数指针重载<<运算符
- 将nullptr参数委托给模板代码中的指针重载
- 用模板和指针重载数组操作符
- STL优先级队列和指针重载