在继承中更改成员类型

时间:2014-09-23 18:14:57

标签: c++ templates inheritance

如果基类Base有两个派生类DerADerB,派生类是否可以在Base成员函数中使用成员变量,但每个班级的类型不同?

class Base {
    * a // Declare a as *something*, so it can be used by "doWork"

    template <typedef T>
    void doWork(T b) { // Add another value to "a" which is of its same type
        a += b; // For example; an operation that works on "a", no matter what numeric type it is
    }
}

class DerA : public Base {
    // Make "a" an int
}

class DerB : public Base {
    // Make "a" a float
}

实际上,a将是一个基本结构,而DerADerB将具有基本结构的派生版本(衍生类将各自具有特定结构的派生形式)为了他们的目的,但每个人都必须在a上做一个简单的操作,所以当我只能使用模板函数时,复制/粘贴每个衍生物的简单函数似乎没有意义。我只需键入a作为基本结构类型,但后来我无法访问每个派生结构所具有的各种专用成员函数和变量(如果我正确理解了继承)。

如果这个问题重复,我道歉,但我不知道这个质量会被称为什么,所以谷歌搜索没有结果。

3 个答案:

答案 0 :(得分:3)

您可能想要的是CRTP

template<class D>
struct Base {
  D* self() { return static_cast<D*>(this); }
  D const* self() const { return static_cast<D*>(this); }
  template<class T>
  void doWork(T b) {
    self()->a += b;
  }
};
struct DerA : public Base<DerA> {
  int a;
};
struct DerB : public Base<DerB> {
  double a;
};

这里我们将派生类型传递给我们的基类。在基类中,您可以使用self()->访问派生类型中的字段。这允许基本上完全访问派生类型,同时让我们在基类中共享代码。

请注意,您无法以DerA方式传递DerBBase。如果您需要,则需要virtual方法doWork,并且virtual template方法不存在。

CRTP代表了奇怪的重复模板模式,我想这个模板是因为它很奇怪,它涉及重复一种类型,并且它会在奇怪的角落里显示为有用。


类型擦除可能也不起作用,因为你想从代码库中的两个不同点发送类型擦除(双重调度问题:你需要一个集中的类型列表,支持类型笛卡尔积)

要对此进行扩展,为了支持a+=ba都是任意类型的b,您必须两次扩展所有类型,包括从不类型在编译单元的相同位置相互可见。这是不可能的。


如果您需要一个公共基础,并且只有一些类型传递给doWork,请按以下步骤操作:

struct Base {
  virtual void doWork( double ) = 0;
  virtual void doWork( int ) = 0;
  virtual void doWork( long long ) = 0;
};

template<class D>
struct Base_helper:Base {
  D* self() { return static_cast<D*>(this); }
  D const* self() const { return static_cast<D*>(this); }
  template<class T>
  void doWork_impl(T b) {
    self()->a += b;
  }
  void doWork( double x ) override { doWork_impl(x); };
  void doWork( int x ) override { doWork_impl(x); };
  void doWork( long long x ) override { doWork_impl(x); };
};
struct DerA : public Base_helper<DerA> {
  int a;
};
struct DerB : public Base_helper<DerB> {
  double a;
};

请注意,doWork的每个版本必须有效才能调用每个Der,因为Base_helper会实例化所有版本。

如果传递给doWork的类型无限制,但Der的类型有限,则可以向后执行上述类似的操作。然而,它变得尴尬。在这种情况下,最好的办法是使用boost::variant类型的解决方案。

答案 1 :(得分:2)

我想你想要实现这样的目标:

template<typedef T>
class Base {
    T a;
    void doWork(T b) { // Add another value to "a" which is of its same type
        a += b; // For example; an operation that works on "a", no matter what numeric type it is
    }
}

class DerA : public Base<int> {
}

class DerB : public Base<float> {
}

或者您可以完全转储类DerA和DerB并改为使用typedef:

typedef Base<int> DerA;
typedef Base<float> DerB;

答案 2 :(得分:2)

这可以通过类似CRTP的模式轻松解决:

template<class D> // Base class is templated
class Base {
public:
    D a;
    void doWork(D b) {
        a += b;
    }
};

class DerA : public Base<int> {};

class DerB : public Base<float> {};

Live Example

编辑:如果您只需要一个公共基础(Base<int>是与Base<float>完全不同的类型),您可以使用接口类并从中继承Base。