2011-02-15

コンストラクターのデリゲート

C++0xにおけるコンストラクターのデリゲートのメモ。

class X
{
public :
    X()
    {
        // 共通の初期化処理
    }

    X( int value ) 
    {
        // 共通の初期化処理
        // 引数を使った追加の処理
    }
} ;

この例では、どちらのコンストラクターも、初期化処理は共通している。同じコードが重複するのは、バグの下である。こういう場合、C++03では、共通の処理をメンバー関数に移すことができる。

class X
{
private :
    void init() ;
public :
    X()
    {
        init() ;
    }

    X( int value ) 
    {
        init() ;
        // 引数を使った追加の処理
    }
} ;

しかし、この方法では、非staticデータメンバーをコンストラクター初期化子で初期化することはできない。それに、結局init関数を呼び出さなければならないことにはかわりない。

class X
{
private :
    int member ;
    void init() ;
public :
    X()
        : member(123) // 重複
    {
        init() ;
    }

    X( int value )
        : member(123) // 重複
    {
        init() ;
        // 引数を使った追加の処理
    }
} ;

コンストラクターを、別のコンストラクターに渡すことができればいいのだが・・・

C++0xでは、コンストラクターのデリゲートが提供されている。以下のように使う。

class X
{
private :
    int member ;
public :
    X()
        : member(123)
    {
        // 初期化処理
    }

    X( int value )
        : X() // コンストラクターのデリゲート
    {
        // 引数を使った追加の処理
    }
} ;

このとき、X::X(int)を、delegating constructorという。デリゲートするコンストラクターは、デリゲート先のコンストラクターを実行してから、コンストラクター本体の実行に移る。コンストラクターをデリゲートする際には、コンストラクター初期化子には、他のメンバーやクラス名を書くことはできない。

もちろん、デリゲートは何度でも行える。

class X
{
public :
    X() : X( 1 ) { std::cout << 1 ; }
    X( int a ) : X( a, 2 ) { std::cout << 2 ; }
    X( int a, int b ) : X( a, b, 3 ) { std::cout << 3 ; }
    X( int a, int b, int c ) { std::cout << 4 ; }
} ;

int main()
{
   X x ;
}

この例では、デフォルトコンストラクターはX::X(int)にデリゲートし、さらに、X::X(int,int)にデリゲートし、さらにX::X(int,int,int)にデリゲートされる。出力は、4321となる。

コンストラクターは、直接的、間接的に、自分自身に対して、デリゲートすることはできない。

class X
{
public :
    X() : X() { } // エラー、直接的な自分自身へのデリゲート

    // エラー、間接的な自分自身へのデリゲート
    X( int ) : X( 0.0 ) { }
    X( double ) : X( 0 ) { } 
} ;

残念ながら現時点で、コンストラクターのデリゲートを実装しているコンパイラーは存在しない。コンストラクターの継承と合わせて、是非とも欲しい機能だ。この機能がなくてはコーディングが困難な現実の問題があるというわけではないが、このような簡単な機能こそ、是非とも普及して欲しい。

No comments: