C ++模板,多态和模板协方差

时间:2017-03-14 19:45:46

标签: c++ templates inheritance wrapper covariance

我有一个"包装"模板看起来像这样:

template <typename T>
class Wrapper {
  virtual T* get() = 0;
}

然后我有以下继承方案:

class A {
  // things
}

class B : public A {
  // more things
}

我有另一个使用该包装器的类:

class Worker {
  void work(Wrapper<A>* wa){
    A* a = wa->get();
    //do stuff with a.
  }
}

可以使用A*完成所有操作{}可以使用B*,但我无法将Wrapper<B>*作为Wrapper<A>*传递给工作人员。有没有办法允许这个?

我已经阅读了有关C ++概念的内容,但我不知道如何解决这个问题。

3 个答案:

答案 0 :(得分:3)

如果每个班级报告其基数:

class A {
  using Base = void;
  // things
}

class B : public A {
  using Base = A;
  // more things
}

你可以做到

template<typename T>
class Wrapper : public EmptyForVoid<T::Base>::Type

使用EmptyForVoid进行基类选择:

template<typename T>
struct EmptyForVoid { using Type = T; };

template<>
struct EmptyForVoid<void> { struct Type {}; };

这将使Wrapper遵循与它们包装的类型相同的继承树。

答案 1 :(得分:-1)

你不能直接这样做,因为C ++没有协方差。在C ++中,默认情况下方法不是虚拟的(与Java不同,我猜C#),目前尚不清楚这是否是一个很棒的想法(语言已经非常复杂)。基本上你要做的就是在work上有一个很薄的模板包装器:

class Worker {
public:
  template <class T>
  void work(Wrapper<T>* wa){
    work_on_a(wa->get());
  }
private:
  void work_on_a(A* a) { // do stuff with a}
}

这仍然是安全的,因为只有在包装类型派生自A或以其他方式定义隐式转换为A时才会编译,否则对work_on_a的函数调用将失败。

你可以在这里做各种优点和缺点的更复杂的事情,但这使代码尽可能简单,并最大限度地减少模板膨胀。

虽然注意,如果work也需要是虚拟的,这将不起作用(因为虚函数不能是模板),并且事情会变得很快。

另一种方法是争辩说包装器根本不应该传递给work;如果所有你需要做的工作都是指向A的指针,那就是你应该传入的所有内容。但是根据你的真实代码可能不是一个选项,或者没有意义。

答案 2 :(得分:-1)

Curiously Recurring Template Pattern的一点点:

class SubBase {
    virtual T* get() { /* default impl. */ }
};

template <typename T>
class Base : public SubBase {};

template <typename T>
class Wrapper : public Base<T> {
    virtual T* get() = 0;
}

...我不知道你的继承图是什么样的,但只要你的包裹的T类型没有get()方法,这应该可行。

<小时/> 编辑:这个简单的片段不起作用,也不是真正的CRTP,正如已经指出的那样;我只能在fairly significant modification之后做一个这种排序工作的例子。