Rab*_*d76 34 c++ object-slicing c++11
I was searching a bug in an application, which I've finally fixed but didn't understand completely. The behavior can be reproduced with the following simple program:
#include <iostream>
#include <memory>
#include <functional>
struct Foo
{
virtual int operator()(void) { return 1; }
};
struct Bar : public Foo
{
virtual int operator()(void) override { return 2; }
};
int main()
{
std::shared_ptr<Foo> p = std::make_shared<Bar>();
std::cout << (*p)() << std::endl;
std::function<int(void)> f;
f = *p;
std::cout << f() << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
The output of the line
Run Code Online (Sandbox Code Playgroud)std::cout << (*p)() << std::endl;
is 2
, which is as I expected, of course.
But the output of the line
Run Code Online (Sandbox Code Playgroud)std::cout << f() << std::endl;
是1
。这让我感到惊讶。我什至惊讶于分配f = *p
是允许的,并且不会导致错误。
我不需要解决方法,因为我通过lambda对其进行了修复。
我的问题是,当我这样做时会发生什么f = *p
,为什么输出1
而不是2
?
我已经重现了gcc(MinGW)和Visual Studio 2019的问题。
此外,我想提及的是
Bar b;
std::function<int(void)> f1 = b;
std::cout << f1() << std::endl;
Run Code Online (Sandbox Code Playgroud)
是2
,一次。
son*_*yao 24
对象切片在这里发生。
给定的点是f = *p;
,p
类型为std::shared_ptr<Foo>
,则类型*p
为Foo&
(而不是Bar&
)。即使是的赋值运算符也std::function
可以通过引用引用参数,但是
4)将目标设置为
*this
可调用对象f
,就像执行一样function(std::forward<F>(f)).swap(*this);
。
注意,F
上面的推论也是Foo&
如此。然后按值接受参数的构造函数,std::function
发生对象切片,效果变为f
从Foo
切片复制的类型的对象分配的效果*p
。
Run Code Online (Sandbox Code Playgroud)template< class F > function( F f );
mol*_*ilo 12
这是常规切片,隐藏在std::function
和层下std::shared_ptr
。
f = *p;
Run Code Online (Sandbox Code Playgroud)
之所以有效,*p
是因为它是一个带有适当对象的可调用对象operator()
,这是你可以包装在一个对象中的东西之一std::function
。
之所以不起作用,是因为它复制了*p
-这是一个Foo&
,而不是一个Bar&
。
您上一个示例的这种改编将具有相同的行为:
Bar b;
Foo& c = b;
std::function<int(void)> f1 = c;
std::cout << f1() << std::endl;
Run Code Online (Sandbox Code Playgroud)
dar*_*une 11
这是切片的情况。原因是std::function
(也如另一个答案所示)的赋值运算符指出:
将* this的目标设置为可调用的f,就像通过执行function(std :: forward(f))。swap(* this);一样。除非参数类型为Args ...且返回类型为R的f可调用,否则此运算符不参与重载解析。(从C ++ 14开始)
https://zh.cppreference.com/w/cpp/utility/functional/function/operator%3D
如果您简化并精简了示例,则可以轻松了解发生了什么:
Foo* p = new Bar;
Foo f;
f = *p;//<-- slicing here since you deref and then copy the object
Run Code Online (Sandbox Code Playgroud)
看来您的目标是获取指向覆盖的虚函数的指针-不幸的是,没有一种简单的方法来展开虚函数查找,因为它是通过运行时查找表实现的。但是,一个简单的解决方法可能是使用lambda进行包装(正如OP也提到的那样):
f = [p]{return (*p)();};
Run Code Online (Sandbox Code Playgroud)
一个更合适的解决方案也可能只是使用reference_wrapper
:
f = std::ref(p);
Run Code Online (Sandbox Code Playgroud)