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

Why passing an object derived from std::function by reference crashes the program?

如果我通过引用将派生自 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?

你的重载没有被使用,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);

std::function::operator()不是虚拟的。

class foo :public std::function<int(void)> {
  public:
    int operator()(){return 1;}
};

foo 视为 std::function 时,您编写的 operator() 没有任何作用。你有一个空 std::function<int()>,而不是 returns 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 自动生成一个 class,它在重要方面几乎与上面的 foo 类型相同。它也没有继承自 std::function。)

C++ 支持不止一种多态性。基于继承的多态性不会(轻易地)允许值类型,而 C++ 在值类型上蓬勃发展,因此 std::function 被编写为与值类型一起工作。


作为@T.C。如下所述,

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

这是可行的,因为编译器在 "slicing" 和基础 class 之间进行选择,并使用转换构造函数,而 C++ 标准首选转换构造函数。

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

这里不是首选,因为不需要做转换,只是"treat as base",比在规则中构造一个新对象优先级更高

在我们传递 lambda 或 "unparented" foo 的情况下,"take a reference to parent" 情况不可用,因此临时 std::function 从我们的 foo(或 lambda)和 f 绑定到它。

bar(f);

您正在将 foo 类型的 f 传递给 bar,但是 bar 的参数是 const std::function<int(void)>

这里 foo 被升级为 const std::function<int(void)>

您没有在 const std::function<int(void)> 中重载 operator()。

所以它抛出运行时错误。