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

C++ 为什么通过引用传递从std::function派生的对象会使程序崩溃?,c++,c++11,std-function,C++,C++11,Std Function,如果我通过引用将一个从std::function派生的对象传递给另一个函数,那么程序在运行时总是会崩溃,并出现错误的函数调用错误。如下代码所示: #include<functional> #include<iostream> class foo :public std::function<int(void)> { public: int operator()(){return 1;} }; void bar(const std::f

如果我通过引用将一个从std::function派生的对象传递给另一个函数,那么程序在运行时总是会崩溃,并出现错误的函数调用错误。如下代码所示:

#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);
}
#包括
#包括
类foo:public std::function{
公众:
int运算符(){return 1;}
};

空条(const std::function&f){std::cout您的重载未使用,const one(
来自std::function
)与
const std::function&f
一起使用。 由于您没有初始化
std::function
,所以它是空的,然后在调用
操作符()
时抛出

当您传递值时,将从您的函子(与任何常规函子一样)创建一个
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;}
};
这可以转换为
std::function
。事实上,通过此更改,您的代码可以编译并工作

如果没有此更改,它更愿意强制转换为base,并将对(空)base
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::函数< /C> >被写入值类型。


如下文所述,@T.C

void bar(std::function<int(void)> f)
void条(标准::函数f)
这是因为编译器在“切片”到基类之间使用转换构造函数,而转换构造函数是C++标准的首选。
void bar(std::function<int(void)> const& f)
void bar(标准::函数const&f)
在这里,它不是首选的,因为不需要进行转换,只需“视为基础”,这比在规则中构造新对象具有更高的优先级

在传递lambda或“unappented”foo的情况下,“take a reference to parent”案例不可用,因此从
foo
(或lambda)创建一个临时
std::function
,并将
f
绑定到它

bar(f);
您正在将
foo
类型的
f
传递到
bar
,但
bar
的参数是
const std::function

这里
foo
被升级为const
std::function

您没有在
const std::function
中重载运算符()


所以它抛出了运行时错误。

为什么要从
std::function
派生。假设函数调用操作符不是虚拟的。我知道从
std::function
派生不是一个好做法。我只是想知道程序为什么会这样。但是当传递值时,
foo
会得到片,不是吗?实际上是这样的类型强制转换为
std::function
(更好的匹配是不相关的)。@IgorR.:这很微妙,调用了
std::function(f)
(不是带切片的复制构造函数)但实际上,我的解释是不准确的。@IgorR.:不,切片是一个只适用于派生类的概念,所以这显然没有发生。它使用了
std::function
转换构造函数,它在内部有效地引用了lambda。@Jarod42 right,这个词“切片”在这里是不正确的。我同意你的分析,但你怎么能明确地知道std::function不是使用继承实现的?这个实现不是已经定义了吗?或者基于继承的解决方案不符合要求有什么原因吗?这里的初始化愚蠢也是“有趣的”".The
const&
直接绑定到基类子对象;按值版本本应调用复制构造函数进行切片,但被转换构造函数劫持。@Nirfiedman它可以使用继承作为内部细节;它可以使用每个编译器在父类内存中对派生类的非标准放置替换。It可以使用unicorns。该标准只指定行为。我要说的是,在标准C++中,您不能使用基于继承的多态性(而不是作为pimpl等价物中的内部细节)来实现
std::function
:您可以使用各种类型擦除策略来实现它(包括在pimpl中对类型擦除使用继承)。它的行为类似于基于类型擦除值的多态性用户代码。
void bar(std::function<int(void)> const& f)
bar(f);