CRTP(Curiously Recurring Template Pattern)を使ってCloneable(Deep copy)を楽に書く方法を考えていた

問題

オブジェクトの深いコピーを作る時は、コピーコンストラクタを内部で呼び出してnewするようなclone関数を作成するのがセオリーだと思い、以下のように書くわけです。

#include <iostream>
//適当なクラス
class Hoge
{
public:
    Hoge* clone(){return new Hoge(*this);}
};
class Moge
{
public:
    Moge* clone(){return new Moge(*this);}
};

int main()
{
    Hoge x1;
    Moge y1;
    //クローンしたオブジェクト
    Hoge *x2 = x1.clone();
    Moge *y2 = y1.clone();
    delete x2;
    delete y2;
    return 0;
}

いちいち書くのが面倒くさい

ただ、上の書き方だとDeep copyさせたいクラスを追加するたびに、いちいちclone関数を各クラスに書かねばならず、面倒くさい。
また仮に「コピー可能クラス・インターフェイス」を作成、継承させてもこの問題は変わらなくて、しかも今の場合、本質的に関係ないと思ってるHoge・Mogeクラスが共にCloneableクラスのオブジェクトとして、格納可能になってしまうのはなんか気持ちが悪い。
一応、書くと以下のような感じか。

#include <iostream>
//適当なクラス
class Cloneable
{
public:
    virtual Cloneable* clone(){return new Cloneable(*this);}
};
class Hoge : public Cloneable
{
public:
    Hoge* clone(){return new Hoge(*this);}
};
class Moge : public Cloneable
{
public:
    Moge* clone(){return new Moge(*this);}
};

int main()
{
    Hoge x1;
    Moge y1;
    //クローンしたオブジェクト
    Hoge *x2 = x1.clone();
    Moge *y2 = y1.clone();
    //Cloneable *x3 = x1.clone();
    //Cloneable *y3 = y1.clone();
    //x3 = y3 が出来てしまう
    delete x2;
    delete y2;
    return 0;
}

この書き方は

とほぼ同じだと思う。

CRTPを使って対処してみる

ここでこのめんどくさい問題に対処するためにはCRTP(Curiously Recurring Template Pattern)を使うのがいいんじゃないかなと考えて書いてみたのが以下のコード。
継承先に何も書かなくてもcloneする事が出来てうれしいなと思うわけです。
もっと良いやり方があるのでしょうかね?

#include <iostream>
//適当なクラス
template <class T>
class Cloneable
{
public:
    T* clone(){return new T(static_cast<T&>(*this));}
};
class Hoge : public Cloneable<Hoge>
{};
class Moge : public Cloneable<Moge>
{};

int main()
{
    Hoge x1;
    Moge y1;
    //クローンしたオブジェクト
    Hoge *x2 = x1.clone();
    Moge *y2 = y1.clone();
    delete x2;
    delete y2;
    return 0;
}

いや、そもそも・・・

テンプレート関数一発書いておけばいいって話?どう使い分けるのが良いのだろうか。
よくわからなくなってきた。

#include <iostream>
//適当なクラス
template <class T>
T* clone(const T& x){return new T(x);}
class Hoge{};
class Moge{};

int main()
{
    Hoge x1;
    Moge y1;
    //クローンしたオブジェクト
    Hoge *x2 = clone(x1);
    Moge *y2 = clone(y1);
    delete x2;
    delete y2;
    return 0;
}