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;
}
The output of the line
std::cout << (*p)() << std::endl;
is 2
, which is as I expected, of course.
But the output of the line
std::cout << f() << std::endl;
is 1
. This was surprising me. I even was surprised that the assignment f = *p
is allowed and doesn't cause an error.
I don't ask for a workaround, because I fixed it by a lambda.
My question is, what is happening when I do f = *p
and why is the output 1
rather than 2
?
I've reproduced the issue with gcc (MinGW) and Visual Studio 2019.
Further I want to mention that the output of
Bar b;
std::function<int(void)> f1 = b;
std::cout << f1() << std::endl;
is 2
, again.
Object slicing happens here.
The point is given f = *p;
, p
is of type std::shared_ptr<Foo>
, then the type of *p
is Foo&
(instead of Bar&
). Even the assignment operator of std::function
takes argument by reference, but
4) Sets the target of
*this
to the callablef
, as if by executingfunction(std::forward<F>(f)).swap(*this);
.
Note that the F
above is deduced as Foo&
too. And the constructor of std::function
takes argument by value, object slicing happens, the effect becomes that f
is assigned from an object of type Foo
which is slice-copied from *p
.
template< class F > function( F f );