2010-11-15

C++0xにすごい変更が来た

Batavia会議は、だいぶ興味深いことになった。詳しくは、正式なNのペーパーが出てから解説するが、とにかく、非常に重要な項目をふたつだけ解説する。

まず、attributeによって提供されていた多くのクラスのメンバーに関する機能は、キーワードを与えられた。これは、言語機能は、特別な文法を与えるに値するという思想からである。会議でコンセンサスの得られた文法は以下の通りである。

struct Base
{
   virtual void f() { }
   int x ;
} ;

struct Derived final explicit : Base
{
   virtual void f() override { }
   int x new ;
} ;

機能はattributeで提供されていたものと全く変わらないので、説明は省く。変更点としては、base_checkのかわりにexplicitを使い、hidingのかわりにnewを使うことになった。newは違和感を覚えるかもしれないが、色々考慮した挙句、こうなった。

ちなみに、C++0xでは、メンバーの宣言にinitializerを記述できるので、その場合、以下のようになるだろう。

int x new = 0 ;

どうも初心者を混乱させそうな気がする。

これらのキーワードは本物のキーワードではない。文脈依存キーワード(contextual keyword)と呼ぶべきものである。つまり、ある場所においてだけ、キーワードとして扱われるのである。これにより、finalやoverrideといったidentifierを使うことができる。これは、下位互換性を保つための処置である。これらのキーワードが出現する場所は、識別子をかけない場所なので、実装は可能である。contextual keywordは、すでにC#で使われており、実績がある。

つぎに、暗黙のコピーコンストラクターという問題がある。このブログを読んでいる読者なら、David Abrahamsの、N3153: Implicit Move Must Go(暗黙のムーブ爆発しろ)というペーパーは、とっくに読んでいることであろう。また、Bjarne Stroustrupの、N3174: To move or not to move(ムブるべきかムブらざるべきか。それが問題じゃ)というペーパーも、すでに熟読しているはずである。

Daveが暗黙のムーブは深刻な互換性の問題を引き起こすからやめるべきだと主張したのに対し、Bjarneは、暗黙のコピーでも問題は同じだと主張した。激戦の結果、Bjarneが勝った。

これは非常に大事なことなので、特別に大きなフォントで書いておく。

暗黙のコピーコンストラクターは、ユーザー定義のデストラクタがある場合、生成される。この挙動はdeprecatedである。

非常に大事なことなので、もう一度書いておく。

暗黙のコピーコンストラクターは、ユーザー定義のデストラクタがある場合、生成される。この挙動はdeprecatedである。

つまり、以下のような意味である。

struct X
{
    ~X() { } // ユーザー定義のデストラクター
} ;

int main()
{
    X a ;
    X b = a ; // 警告! deprecated!
}

クラスXには、暗黙のコピーコンストラクターが生成される。ただし、この挙動はdeprecatedである。将来的には、規格から削除されるかもしれない。上記のようなコードを書いてはいけない。

ではどうするか。もちろん、ユーザー定義のコピーコンストラクターを書くこともできる。しかし、もし暗黙のコピーコンストラクターの挙動で問題がないのならば、explicitly-defaultedにすることもできる。

struct X
{
    X( X const & ) = default ; // 明示的なデフォルト化
    ~X() { } // ユーザー定義のデストラクター
} ;

int main()
{
    X a ; X b ;
    a = b ; // 正しいコード
}

したがって、正しいC++プログラマーは、ユーザー定義のデストラクターがある場合には、暗黙のコピーコンストラクターは生成されないものと考えるべきである。その機能はdeprecatedである。deprecatedな機能は使うべきではない。

ちなみに、暗黙のコピーコンストラクターは、ユーザー定義のムーブコンストラクターや、ムーブ代入演算子がある場合にも生成されるが、これも同じ理由でdeprecatedである。

5 comments:

Anonymous said...

死ね

QUWAHARA said...

「暗黙のコピーコンストラクターは~」は大変勉強になりました

萌ゑ said...

あーあ・・・現行の部分的にC++0xに対応しているコンパイラの開発者は頭を抱えるでしょうね。

Anonymous said...

deprecated というのがまた微妙ですねえ。どうせダメなら prohibited と言い切ってほしい。

江添亮 said...

>現行の部分的にC++0xに対応しているコンパイラの開発者は頭を抱えるでしょうね。

そうでもないでしょう。適切な対応は、単に条件を満たした場合、warningを出すだけです。

>deprecated というのがまた微妙ですねえ。どうせダメなら prohibited と言い切ってほしい。

互換性は重要です。なにもしないけれど、とりあえず空のデストラクターを書いているコードは、五万とあるでしょう。また、継承を使った際に、クラスのデストラクターを正しく呼び出す目的で、とくにデストラクター内の処理はないけれど、virtualデストラクターにするためだけに、空のデストラクターを書くこともあります。

今までは、ユーザー定義デストラクターの存在は、暗黙のコピーコンストラクターの生成を妨げなかったので、これらのコードは、C++03では合法なコードです。
これを、今壊すわけには行きません。
プログラマーの意識を変わるまで、何十年もかかるでしょう。
まあ、プログラマーの意識が変わるまでというよりは、時代についていけないプログラマーが業界からいなくなるまでといったほうが適切なのかもしれませんが。