更改继承中的成员类型

Changing member types in inheritance

本文关键字:成员 成员类 类型 继承      更新时间:2023-10-16

给定一个基类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 作为基本结构类型,但随后我无法访问每个派生结构具有的各种专用成员函数和变量(如果我正确理解继承(。

如果这个问题是重复的,我很抱歉,但我不知道这种质量会叫什么,所以谷歌搜索被证明是徒劳的。

你可能想要的是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()->访问派生类型中的字段。 这基本上允许对派生类型进行完全访问,同时允许我们在基类中共享代码。

请注意,您不能以这种方式传递DerADerB作为Base。 如果你想要这个,你需要一个virtual方法doWork,而virtual template方法不存在。

CRTP代表奇怪的重复模板模式,我想之所以命名,是因为它很奇怪,它涉及重复一种类型,并且它不断出现在奇怪的角落,因为它很有用。


类型擦除

可能也不起作用,因为您希望从代码库中的两个不同位置调度类型擦除(双重调度问题:您需要一个集中的类型列表来支持执行笛卡尔乘积类型(。

为了

扩展这一点,为了支持ab都是任意类型的a+=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类型的解决方案。

我想你想实现这样的事情:

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 并改用 typedefs:

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

这可以通过类似 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> {};

现场示例

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