2010-09-05

deleted definitionによるクラスの初期化の制御

bool型で初期化したいクラスがあるとする。

struct Boolen
{
    Boolen( bool ) { }
} ;

残念ながら、このクラスは、あまり宜しくない。なぜならば、C++には、忌々しい暗黙の型変換というものがあるからだ。

int main()
{
    Boolen a = true ; // OK、当然だ
    Boolen b = 123 ; // OK、ハァ?
    Boolen c = &a ; // OK、おいおい、おかしいだろ常識的に考えて
}

このような馬鹿げたコードは、コンパイルエラーになって欲しい。もし、数値やポインターをboolとして扱いたいのであれば、明示的にキャストするべきなのだ。

int main()
{
    Boolen a = true ; // OK、当然だ
    Boolen b = bool( 123 ) ; // 自分が何をしているのかは十分承知している
    Boolen c = bool( &a ) ; // 時にはこんな汚いコードも必要なのだ
}

しかし、現実は汚い。というのも、数値やポインターからboolへの変換というのは、あまりにも基本的すぎる暗黙の型変換であり、到底C++から取り除く事はできないのだ。多くのコンパイラーは、警告さえ発しない。というのも、あまりにも根本的すぎる暗黙の型変換のため、心ないプログラマーによって欠かれたコードに対して、警告を出すと、あまりに大量の警告がでてしまい、警告の意味をなさなくなってしまうからである。結果として、コンパイラーが文句を言わないため、世のクソプログラマーは、このような不自然極まりないコードに対しても、明示的なキャストを書かない。

このきたならしい阿呆がァーーッ!!

そういう輩は、痛い目を見る必要がある。幸い、C++0xには、このようなきたならしい阿呆に痛い目をみせることのできる機能が追加された。deleted definitionsである。この機能は、定義を削除する機能である。定義を削除された関数は、宣言以外の用途で使うことはできない。

おれは人間をやめるぞ!
ジョジョーーーーッ!!
おれは人間を超越するッ!

struct Boolen
{
    Boolen( ) = delete ;// これは無くてもよい
    Boolen( bool ) { }

    template < typename T >
    Boolen( T ) = delete ;
} ;

int main()
{
    Boolen a = true ; // OK
    Boolen b = 0 ; // エラー
    Boolen c = nullptr ; // エラー
}

これは、厳密に言えば、暗黙の型変換を禁止するコードではない。このように書けば、bool以外の型での初期化は、テンプレートのインスタンス化とオーバーロード解決により、関数テンプレート版のコンストラクターが選ばれる。しかし、関数テンプレート版のコンストラクターの定義は削除されているので、使うとエラーになる。結果として、暗黙の型変換を禁止している。

この程度のことは、C++0xならば、できて当然である。

空気を吸って吐くことのように!
HBの鉛筆をベキッ!と
へし折る事と同じようにッ
できて当然と思うことですじゃ!

この、使うとエラーになるというのは、宣言以外のあらゆる利用に該当する。関数の呼び出し、ポインターやリファレンスの取得はもちろん、たとえ未評価式の中でも、変わることがない。

// 削除された関数の定義
void f() = delete ;

void f() ; // OK、宣言はできる

int main()
{
    f() ; // エラー、削除された関数の呼び出し
    &f ; // エラー、削除された関数のポインターを得ようとしている
    void (& ref )(void) = f ; // エラー、削除された関数のリファレンスを得ようとしている
    typedef decltype(f) type ; // エラー、未評価式の中で、削除された関数を参照している
}

ちなみに、現行のgccにはバグがあり、なぜか再宣言すると、エラーの内容が、「削除された関数を使っている」から、「定義がない」というものに変わってしまう。また、最後の例が、エラーなくコンパイル出来てしまう。この問題はすでに報告した。

deleted definitionは、実に面白い。

2 comments:

Anonymous said...

へー、C++0xではこんなものが。
あ、最後のコードに誤字があります。typdef→typedefですね。

Anonymous said...

Boolenの例は現行仕様でもexplicitとオペレーターのオーバーロードでエラーにできますね。