Search code examples
c++c++11object-slicing

Unexpected behavior after assignment of function object to function wrapper


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.


Solution

  • 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 callable f, as if by executing function(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 );