Virtual functions and std::function?

房东的猫 提交于 2019-12-11 02:13:01

问题


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?


回答1:


You can use a std::reference_wrapper, or the convenient std::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.

std::function<void()> f1(std::ref(*ptr1));
std::function<void()> f2(std::ref(*ptr2));



回答2:


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?

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:

std::function f1([ptr1](){ (*ptr1)(); });
std::function f2([ptr2](){ (*ptr2)(); });



回答3:


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 to std::function.




回答4:


A simple way to avoid the slicing copy made by std::function is to bind the method operator() to the object it should be applied.

So you could write:

auto f1 = std::bind(&base::operator(), ptr1);
auto f2 = std::bind(&base::operator(), ptr2);
std::invoke(f1);                          // calls derived1::operator()
std::invoke(f2);                          // calls derived2::operator()


来源:https://stackoverflow.com/questions/47986978/virtual-functions-and-stdfunction

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!