如果我通过引用将从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);
}
但是,如果函数对象按值传递 ,如下所示:
void bar(std::function<int(void)> f)
程序运行正常。我在gcc,clang和visual studio上测试了这个程序,结果是一样的。导致这个bad_function_call的原因是什么?
答案 0 :(得分:8)
std::function::operator()
不是虚拟的。
class foo :public std::function<int(void)> {
public:
int operator()(){return 1;}
};
将foo
视为std::function
时,您撰写的operator()
会 。您有一个空std::function<int()>
,而不是一个返回1。
std::function
基于类型擦除的多态性,而不是基于继承的多态性。它可以存储它可以调用,复制和销毁的任何东西。您可以通过值传递,并且存储的callable将随之带来。
继承它通常不是你想要做的。
class foo {
public:
int operator()(){return 1;}
};
这可以转换为std::function
。实际上,通过此更改,您的代码可以编译并运行。
如果没有这个更改,它更喜欢强制转换为基础并将对(空)基础std::function
的引用传递给参数。如果没有继承,它会尝试将foo
转换为std::function
并成功。
当然,这个foo
非常愚蠢。
而不是:
int main(){
foo f;
bar(f);
}
我们可以这样做:
int main(){
auto f = []{ return 1; };
bar(f);
}
它的效果也一样。 (上面的lambda在重要方面自动生成一个几乎与上面foo
类型相同的类。它也不会从std::function
继承。)
C ++支持多种多态。基于继承的多态不会(容易地)允许值类型,并且C ++在值类型上茁壮成长,因此编写std::function
以使用值类型。
As @ T.C。如下所述,
void bar(std::function<int(void)> f)
这是有效的,因为编译器正在选择&#34;切片&#34;到基类并使用转换构造函数,转换构造函数是C ++标准的首选。
void bar(std::function<int(void)> const& f)
这里不是首选,因为不需要进行任何转换,只需将其视为基础&#34;,这比在规则中构建新对象具有更高的优先级。
在我们传递lambda或&#34;未授权的&#34; foo,&#34;引用父母&#34;情况不可用,因此我们的std::function
(或lambda)和foo
绑定了一个临时f
。
答案 1 :(得分:4)
未使用过载,const {(from std::function
)与const std::function<int(void)>& f
一起使用。
当你没有初始化std::function
时,它是空的,然后在调用operator ()
时抛出。
当您通过值传递时,会从您的仿函数(与任何常规仿函数一起)创建std::function
,然后std::function
调用您存储的仿函数。
但不要从std::function
派生,只需使用
auto f = [](){ return 1; };
bar(f);
答案 2 :(得分:1)
bar(f);
您将f
类型的foo
传递给bar
,但bar
的参数为const std::function<int(void)>
此处foo
已升级到const std::function<int(void)>
。
您没有在const std::function<int(void)>
中重载operator()。
所以它会引发运行时错误。