为何std::function<void(T)>可赋值给std::function<void(T const&)>却不可给std::function<void(T&)>?
std::function参数类型赋值兼容问题解析
先看这段示例代码:
#include <functional> class Foo {}; int main() { std::function<void(Foo)> func; std::function<void(Foo)> f1; std::function<void(Foo const&)> f2; std::function<void(Foo&)> f3; func = f1; func = f2; func = f3; // error: no match for ‘operator=’ (operand types are ‘std::function<void(Foo)>’ and ‘std::function<void(Foo&)>’) return 0; }
这段代码中,std::function<void(Foo)>类型的func能正常赋值给同类型的f1,也能赋值给std::function<void(Foo const&)>类型的f2,但赋值给std::function<void(Foo&)>类型的f3时会触发编译错误,核心原因在于std::function的赋值兼容规则是基于调用安全性判断的:
为什么func可以赋值给f2?
func的签名是void(Foo),意味着调用它时需要传入Foo类型的实参(值传递);而f2的签名是void(Foo const&),接受的是Foo的const左值引用。
在C++中,任何Foo类型的对象(包括值传递产生的临时对象)都可以合法绑定到Foo const&引用上:
- 值传递的实参要么是拷贝后的对象,要么是临时对象,const引用可以延长临时对象的生命周期,且调用过程中不会修改实参,完全符合类型安全要求。
- 从调用逻辑看,
void(Foo)包装的可调用对象,完全能被void(Foo const&)的签名调用——只需要把值实参绑定到const引用即可,没有任何安全风险,因此这种赋值被允许。
为什么func不能赋值给f3?
f3的签名是void(Foo&),需要的是非const左值引用。而func的签名调用时,传入的实参是值类型,会生成临时的Foo对象(右值)。
C++明确禁止临时对象绑定到非const左值引用:
- 临时对象的生命周期极短,绑定到非const引用后如果对其修改,很可能导致未定义行为;
- 换句话说,
std::function<void(Foo)>包装的可调用对象,无法满足void(Foo&)的调用要求——后者需要一个可修改的左值,而前者只能提供右值,这种调用本身就是非法的,因此std::function的赋值操作直接禁止了这种不兼容的类型转换。
内容的提问来源于stack exchange,提问作者DarkLight
相关产品推荐
相关产品推荐

