2011-07-24

テンプレートはコピー、ムーブではない

この問題だけを完結にまとめた文章が必要であると感じたので書く。

テンプレートなコンストラクターや代入演算子は、コピー/ムーブコンストラクター、コピー/ムーブ代入演算子とはみなされない。したがって、テンプレートなコンストラクターや代入演算子の存在は、暗黙のコピー/ムーブコンストラクターやコピー/ムーブ代入演算子の生成を妨げない。

ただし、テンプレートなコンストラクターや代入演算子は、コピー、ムーブが必要な場合に、オーバーロード解決の候補になる。

struct X
{
    X() { }

    template < typename T >
    X( T & ) { }

    // 以下の暗黙のコピーコンストラクターが生成される
    // X( X const & ) ;
} ;

int main()
{
    X a ;
    X const b( a ) ; // テンプレートコンストラクター
    X c( b ) ; // 暗黙のコピーコンストラクタ―
}

bの初期化では、aは非constなオブジェクトであるので、オーバーロード解決により、テンプレートコンストラクターが選ばれる。cの初期化では、bはconstであるので、オーバーロード解決により、非テンプレートな暗黙のコピーコンストラクターが選ばれる。

もし、この挙動が嫌であれば、コピーコンストラクターを明示的にdelete定義しておけばよい。

struct X
{
    // 暗黙のコピーコンストラクターの生成を阻害
    X( X const & ) = delete ;
}

クラスXのコンストラクターは、Xを引数に取ることはできない。

struct X
{
    X(X) ; // エラー
} ;

ただし、テンプレートの場合、インスタンス化の結果がこのようなシグネチャになる場合、インスタンス化されない。

struct X
{
    X() { }

    template < typename T >
    X( T ) { }
} ;

int main()
{
    X a ;
    X b( a ) ; // 暗黙のコピーコンストラクター
}

したがって、テンプレートなコンストラクターを、安心して書くことができる。

代入演算子も、同様である。テンプレートな代入演算子は、コピー代入演算子ではない。ただし、コピーが必要な場面では、テンプレートな代入演算子も、オーバーロード解決の候補になる。

No comments: