Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

remove type from function with template return value

Tags:

c++

c++11

I have the following classes:

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) { ... }
};

Now, if I want to clone B, I do the following:

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

Is there any way to remove the template type without re-implementing Clone in each and every derived classes?

I want something like this:

B b1;
B* b2 = b1.Clone();
like image 469
arash kordi Avatar asked May 09 '17 06:05

arash kordi


1 Answers

The way to do this is with 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) { ... }
};

These 3 level hierarchies are quite common. Basically, the top class is pure interface, as before (note: you should = 0 the other functions too). The middle class uses the CRTP pattern: it is templated on the derived typed. the idea is that by having static access to the derived type, it can automatically implement things like Clone. Then the derived type implements any implementation that cannot be done generically.

Notice that the derived-most type inherits from the CRTP class templated on itself. That's where the name comes from (Curiously Recurring Template Pattern). Of course, since inheritance is transitive B also inherits from A still, as originally, enabling the same sort of things.

Here is a full working example that you can execute: http://coliru.stacked-crooked.com/a/8f2b201a06b5abcc. I kept the code in the answer as similar to the question as possible, but in the coliru example there are a few small but important differences:

  • usage of owning pointers instead of raw pointers considered good practice in C++, and because smart pointers are not covariant this affects the signatures
  • correct use of = 0 and override, as well as const
  • an example of the static downcast which is kind of a signature of CRTP that didn't come up with your example
like image 156
Nir Friedman Avatar answered Oct 25 '22 14:10

Nir Friedman