如何通过多态基础 class 接口使用模板化子 classes

How to use templated child classes by polymorphic base class interface

我有基本的父 class 和模板化的子 class。 我想使用集合中的 Childs 通过父级的多态接口枚举它们。 我希望在那里调用虚拟(多态)函数 - 但我只有对 Parent::print()

的静态类型调用
#include <iostream>
#include <vector>
using namespace std;

class Parent {
public:
    Parent() { cout << " parent ctor "; }
    virtual void print() { cout << " Parent::print "; }
};


template <typename T>
class Child : public Parent {
public:
    Child(T value) : var(value) { cout << " child ctor "; }
    virtual void print() { cout << " Child::print " << var; }
protected:
    T var;
};

int main() {
    Child<int> myChild(1);
    Child<double> myDoubleChild(2.);

    vector<Parent> v = {myChild, myDoubleChild};

    for (auto i : v) {
        i.print();
    }

    return 0;
}

实际输出:

 parent ctor  child ctor  parent ctor  child ctor  Parent::print  Parent::print 

预期输出应包含 "Child::print" 虚函数调用

原因是Object Slicing
即:

when you assign an object of a subclass to the super class. The superclass knows nothing of the additional information in the subclass, and hasn't got room to store it, so the additional information gets "sliced off"

C++ 中的多态性仅适用于引用和指针。 vector<Parent> 是一个值向量。它们被构造为 Child 实例的副本,并且都是 Parent 类型。基本上它的作用是这样的:

vector<Parent> v = { Parent(myChild), Parent(myDoubleChild) };

如果您将 Parent 设为抽象 class(例如,将 print 更改为纯虚方法),您会看到错误。

您需要一个指针向量,例如:

vector<unique_ptr<Parent>>

如@tchelidze 和@StenSoft 所述,存在两个缺陷:

  1. 对象切片。蹩脚的错误。 std::vector 使用原始对象
  2. 多态性仅适用于指针或引用

启用动态调度代码应该是这样的:

#include <iostream>
#include <vector>
using namespace std;

class Parent {
public:
    Parent() { cout << " parent ctor "; }
    virtual void print() { cout << " Parent::print "; }
};


template <typename T>
class Child : public Parent {
public:
    Child(T value) : var(value) { cout << " child ctor "; }
    virtual void print() { cout << " Child::print " << var; }
protected:
    T var;
};

int main() {
    Child<int> myChild(1);
    Child<double> myDoubleChild(2.);

//    vector<Parent> v = {myChild, myDoubleChild};
    vector<Parent*> v = {&myChild, &myDoubleChild};

    for (auto i : v) {
//        i.print();
        i->print();
    }
    return 0;
}

这给出了所需的输出:

parent ctor  child ctor  parent ctor  child ctor  Child::print 1 Child::print 2

如前所述,对象切片是问题所在。使用原始指针向量的替代方法是使用 std::reference_wrapper.

的向量

std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references

#include <iostream>
#include <functional>
#include <vector>
using namespace std;

class Parent {
public:
    Parent() { cout << " parent ctor "; }
    virtual void print() { cout << " Parent::print "; }
};


template <typename T>
class Child : public Parent {
public:
    Child(T value) : var(value) { cout << " child ctor "; }
    virtual void print() { cout << " Child::print " << var; }
protected:
    T var;
};

int main() {
    Child<int> myChild(1);
    Child<double> myDoubleChild(2.);

    typedef std::reference_wrapper<Parent> refType;

    vector<refType> v = {refType(myChild), refType(myDoubleChild)};

    for (auto i : v) {
        i.get().print();
    }
    return 0;
}

这段代码的输出是:

parent ctor  child ctor  parent ctor  child ctor  Child::print 1 Child::print 2

它不是那么干净(仍然需要特殊的语法)但可能被认为是更地道的 C++。