Consider the following code in C++17:
#include <iostream>
#include <functional>
struct base
{
base() {std::cout << "base::base" << std::endl;}
virtual ~base() {std::cout << "base::~base" << std::endl;}
virtual void operator()() {std::cout << "base::operator()" << std::endl;}
};
struct derived1: base
{
derived1() {std::cout << "derived1::derived1" << std::endl;}
virtual ~derived1() {std::cout << "derived1::~derived1" << std::endl;}
virtual void operator()() {std::cout << "derived1::operator()" << std::endl;}
};
struct derived2: base
{
derived2() {std::cout << "derived2::derived2" << std::endl;}
virtual ~derived2() {std::cout << "derived2::~derived2" << std::endl;}
virtual void operator()() {std::cout << "derived2::operator()" << std::endl;}
};
int main(int argc, char* argv[])
{
base* ptr1 = new derived1();
base* ptr2 = new derived2();
std::function f1(*ptr1);
std::function f2(*ptr2);
std::invoke(*ptr1); // calls derived1::operator()
std::invoke(*ptr2); // calls derived2::operator()
std::invoke(f1); // calls base::operator()
std::invoke(f2); // calls base::operator()
delete ptr1;
delete ptr2;
return 0;
}
std::function
does not seem to do the right thing with virtual functions. Would there be any way to make std::invoke(*ptrN)
and std::invoke(fN)
behave the same way? Or would there be any way to create a new function wrapper that would deal with virtual functions?
your code doesn't pass instance of derived class to
std::function
, instead, it constructs new base object by copying instance of derived, which is passed tostd::function
.std::function
copies its arguments to be able to run later, as already suggested in the comments to the question.Therefore you can just avoid slicing objects and copy something else that is able to deal correctly with polymorphism.
As an example:
A simple way to avoid the slicing copy made by
std::function
is to bind the methodoperator()
to the object it should be applied.So you could write:
You can use a
std::reference_wrapper
, or the convenientstd::ref
.std::function
will use SOO (small object optimization) in this case, so the object won't be copied/moved (avoiding the slicing problem). However, you won't get the deduction guide so you need to specify the template arguments.