使用模板返回值从函数中删除类型

时间:2017-05-09 06:50:25

标签: c++ c++11

我有以下课程:

class A {
public:
  virtual std::string Serialize();
  virtual void Deserialize(std::string);

  template <typename T>
  T* Clone()
  {
    std::string s = Serialize();
    T* t = new T();
    t->Deserialize(s);
    return t;
  }
};

class B : public A {
public:
  std::string Serialize() { ... }
  void Deserialize(std::string) { ... }
};

现在,如果我想克隆B,我会执行以下操作:

B b1;
B* b2 = b1.Clone<B>();

有没有办法删除模板类型而不在每个派生类中重新实现Clone

我想要这样的事情:

B b1;
B* b2 = b1.Clone();

1 个答案:

答案 0 :(得分:12)

执行此操作的方法是使用CRTP:

class A {
public:
    virtual std::string Serialize();
    virtual void Deserialize(std::string);
    virtual A* Clone() = 0;  
};

template <class T>
class HelperA : public A {

    T* Clone() override
        {
            std::string s = Serialize();
            T* t = new T();
            t->Deserialize(s);
            return t;
        }
};

class B : public HelperA<B> {
public:
    std::string Serialize() { ... }
    void Deserialize(std::string) { ... }
};

这三个层次结构很常见。基本上,顶级类是纯接口,如前所述(注意:你应该= 0其他函数)。中间类使用CRTP模式:它是在派生类型上模板化的。我们的想法是,通过对派生类型进行静态访问,它可以自动实现Clone之类的内容。然后派生类型实现了一般无法完成的任何实现。

请注意,派生最多类型继承自模板化的CRTP类。这就是名称的来源(奇怪的重复模板模式)。当然,由于继承是传递性的,B也继承了A,原来它仍然能够实现同样的东西。

以下是您可以执行的完整工作示例:http://coliru.stacked-crooked.com/a/8f2b201a06b5abcc。我将答案中的代码尽可能地与问题类似,但在coliru示例中,存在一些小而重要的差异:

  • 使用拥有指针代替原始指针被认为是C ++中的良好实践,并且因为智能指针不是协变的,这会影响签名
  • 正确使用= 0和覆盖,以及const
  • 静态向下广播的一个例子,它是一种没有提出你的例子的CRTP签名