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