qft*_*qft 3 c++ c++11 std-function
如果我通过引用将从std :: function派生的对象传递给另一个函数,则程序在运行时始终会因bad_function_call错误而崩溃.如下面的代码所示:
#include<functional>
#include<iostream>
class foo :public std::function<int(void)> {
public:
int operator()(){return 1;}
};
void bar(const std::function<int(void)>& f) {std::cout << f() << std::endl;}
int main(){
foo f;
bar(f);
}
Run Code Online (Sandbox Code Playgroud)
但是,如果函数对象通过值传递,如下所示:
void bar(std::function<int(void)> f)
Run Code Online (Sandbox Code Playgroud)
程序运行正常.我在gcc,clang和visual studio上测试了这个程序,结果是一样的.导致这个bad_function_call的原因是什么?
std::function::operator() 不是虚拟的.
class foo :public std::function<int(void)> {
public:
int operator()(){return 1;}
};
Run Code Online (Sandbox Code Playgroud)
观看时foo的std::function,在operator()你写的确实没什么.你有一个空的std::function<int()>,而不是一个返回1.
std::function基于类型擦除的多态,而不是基于继承的多态.它可以存储任何可以调用,复制和销毁的东西.您可以按值传递它,并且存储的可调用对象将随之传递.
继承它往往不是你想要做的.
class foo {
public:
int operator()(){return 1;}
};
Run Code Online (Sandbox Code Playgroud)
这可以转换为std::function.实际上,通过此更改,您的代码可以编译并运行.
如果没有这个改变,它更喜欢cast-to-base并将对(empty)base的引用传递std::function给参数.没有继承,它会尝试将转换foo为a std::function并成功.
当然,这foo很愚蠢.
而不是这个:
int main(){
foo f;
bar(f);
}
Run Code Online (Sandbox Code Playgroud)
我们做得到:
int main(){
auto f = []{ return 1; };
bar(f);
}
Run Code Online (Sandbox Code Playgroud)
它的工作原理也一样.(上面的lambda自动生成一个类,foo它在重要方面几乎与上面的类型相同.它不会从std::function任何一个继承.)
C++支持多种多态.基于继承的多态性不(容易)允许值类型,并且C++在值类型上蓬勃发展,因此std::function编写为使用值类型.
正如下面提到的@TC,
void bar(std::function<int(void)> f)
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为编译器在"切片"到基类和使用转换构造函数之间进行选择,并且转换构造函数是C++标准的首选.
void bar(std::function<int(void)> const& f)
Run Code Online (Sandbox Code Playgroud)
这里不是首选,因为不需要进行转换,只需"将其视为基础",这比在规则中构造新对象具有更高的优先级.
在我们传递lambda或"无代表"foo的情况下,"引用父类"的情况不可用,因此std::function从我们foo(或lambda)创建临时并f绑定它.