为什么通过引用传递从std :: function派生的对象会导致程序崩溃?

时间:2017-04-11 17:00:51

标签: 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);
}

但是,如果函数对象按值传递 ,如下所示:

void bar(std::function<int(void)> f)

程序运行正常。我在gcc,clang和visual studio上测试了这个程序,结果是一样的。导致这个bad_function_call的原因是什么?

3 个答案:

答案 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()。

所以它会引发运行时错误。