如果我通过引用将派生自 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?
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
执行基于类型擦除的多态性,而不是基于继承的多态性。 它可以存储任何可以调用、复制和销毁的内容。 您可以按值传递它,存储的可调用对象将随之而来。
从中继承通常不是您想要做的。
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
是为了处理值类型。
如下@T所述,
void bar(std::function<int(void)> f)
这是有效的,因为编译器在"切片"到基类和使用转换构造函数之间进行选择,并且转换构造函数是C++标准的首选。
void bar(std::function<int(void)> const& f)
这里不是首选,因为不需要进行转换,只需"视为基础",这比在规则中构造新对象的优先级更高。
在我们传递 lambda 或"非父级"foo 的情况下,"引用父级"的情况不可用,因此从我们的foo
(或 lambda)创建一个临时std::function
并f
绑定到它。
你的重载没有被使用,常量一个(from std::function
)与const std::function<int(void)>& f
一起使用。 由于您不初始化std::function
,它是空的,然后在调用operator ()
时抛出。
按值传递时,将从函子(与任何常规函子一样)创建一个std::function
,然后std::function
调用存储的函子。
但不要从std::function
派生,只是使用
auto f = [](){ return 1; };
bar(f);
bar(f);
您正在将类型foo
的f
传递给bar
,但bar
的参数const std::function<int(void)>
在这里,foo
被上升为常量std::function<int(void)>
。
您没有在const std::function<int(void)>
中重载运算符()。
所以它抛出运行时错误。