虚函数和std :: function?

问题描述 投票:2回答:4

考虑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似乎没有使用虚函数做正确的事情。有没有办法让std::invoke(*ptrN)std::invoke(fN)表现得一样?或者有没有办法创建一个新的函数包装器来处理虚函数?

c++ oop templates c++17 virtual-functions
4个回答
3
投票

你可以使用std::reference_wrapper,或方便的std::ref。在这种情况下,std::function将使用SOO(小对象优化),因此不会复制/移动对象(避免使用slicing problem)。但是,您不会获得演绎指南,因此您需要指定模板参数。

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

3
投票

有没有办法使std :: invoke(* ptrN)和std :: invoke(fN)的行为方式相同?或者有没有办法创建一个新的函数包装器来处理虚函数?

正如问题评论中已经提出的那样,std::function复制其论点以便能够稍后运行。 因此,您可以避免切片对象并复制能够正确处理多态的其他内容。 举个例子:

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

1
投票

您的代码不会将派生类的实例传递给std::function,而是通过复制派生的实例来构造新的基础对象,该实例将传递给std::function


0
投票

避免std::function进行切片复制的一种简单方法是将方法operator()绑定到应该应用的对象。

所以你可以写:

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()
© www.soinside.com 2019 - 2024. All rights reserved.