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

ara*_*rdi 14 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) { ... }
};
Run Code Online (Sandbox Code Playgroud)

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

B b1;
B* b2 = b1.Clone<B>();
Run Code Online (Sandbox Code Playgroud)

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

我想要这样的东西:

B b1;
B* b2 = b1.Clone();
Run Code Online (Sandbox Code Playgroud)

Nir*_*man 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) { ... }
};
Run Code Online (Sandbox Code Playgroud)

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

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

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

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