Unexpected behavior after assignment of function object to function wrapper
This is regular slicing, hidden under a layer of std::function
and std::shared_ptr
.
f = *p;
is valid because *p
is a callable object with an appropriate operator()
, and that is one of the things you can wrap in a std::function
.
The reason that it doesn't work is that it copies *p
– and that is a Foo&
, not a Bar&
.
This adaptation of your last example would behave the same:
Bar b;
Foo& c = b;
std::function<int(void)> f1 = c;
std::cout << f1() << std::endl;
Slicing
This is a case of slicing.
The reason is assignment operator of std::function
(as demonstrated in another answer as well) which states:
Sets the target of *this to the callable f, as if by executing function(std::forward(f)).swap(*this);. This operator does not participate in overload resolution unless f is Callable for argument types Args... and return type R. (since C++14)
https://en.cppreference.com/w/cpp/utility/functional/function/operator%3D
If you simplify and strip down the example - you can easily see what's going on:
Foo* p = new Bar;
Foo f;
f = *p;//<-- slicing here since you deref and then copy the object
It looks like you were aiming at obtaining a pointer to the overridden virtual function - unfortunately, theres no easy way to unroll the virtual function lookup as that is implemented via a runtime lookup table. However an easy workaround might be to use a lambda to wrap (As the OP also mentions):
f = [p]{return (*p)();};
A more suitable solution could also be to just a use reference_wrapper
:
f = std::ref(p);
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 );