追記。正しくは、例外オブジェクトは、コピーかムーブのどちらかをサポートしていなければならないというものであった。つまり、コピーするのならば、ムーブは必要ないし、ムーブしかできないクラスも、例外オブジェクトたりえる。したがって、この記事の内容は誤りである。
例外として投げるオブジェクトのことを、例外オブジェクトという。例外オブジェクトがクラスのオブジェクトである場合、コピーコンストラクター、ムーブコンストラクター、デストラクターにアクセス可能でなければならない。(たとえ、使われなかったとしても)
struct X { X() = default ; X( X const & ) = delete ; } ; void f() { throw X() ; // エラー、コピーコンストラクターにアクセスできない }
しかし、これは非常にわかりにくいと思う。例えば、以下のコードは誤りである。
struct X { X() = default ; X( X const & ) { } // ユーザー定義コピーコンストラクター } ; void f() { throw X() ; // エラー、ムーブコンストラクターにアクセスできない }
なぜかというと、ユーザー定義のコピーコンストラクターがある場合、ムーブコンストラクターは暗黙にdefault化されないからだ。よって、エラーになる。どうやら、今のC++コンパイラーは、まだこの挙動を正しく実装していないようだ。
これは、コピーコンストラクターに限った話ではない。ユーザー定義のコピー代入演算子や、ムーブ代入演算子、デストラクターがある場合、ムーブコンストラクターは暗黙にdefault化されない。
また、暗黙のコピーコンストラクターは、ユーザー定義のコピー代入演算子や、ムーブコンストラクター、ムーブ代入演算子、デストラクターがある場合、default化される。しかし、この挙動はdeprecatedである。将来的には、廃止されるだろう。とすると、将来、C++の改定されたときに、以下のコードは動かなくなる可能性がある。
struct X { X() = default ; X( X && ) { } // ユーザー定義のムーブコンストラクター } ; int main() { throw X() ; // C++0xではOK。ただし、将来的には不安。 }
もちろん、これは例外に限った話ではない。C++0xで、暗黙の特別なメンバー関数が欲しい場合は、明示的にdefault化すべきだろう。
> コピーコンストラクター、ムーブコンストラクター、デストラクターに
ReplyDelete> アクセス可能でなければならない。
コピーできてムーブできないクラスを例外オブジェクトとして禁じる理由が
思い当たりませんし、これが正しいとすると既存の C++03 向けコードが
エラーになってしまう場面が多く考えられます。
規格の意図としては、ライブラリが要求する MoveConstructible と同様に
rvalue による初期化でムーブかコピーのいずれかが可能であることを要求
したいだけであり、文面が間違っていると考えるのが妥当ではないでしょうか?
一応、ムーブコンストラクターが暗黙にも明示的にも宣言されていないときは、ムーブコンストラクターを呼び出す式は、コピーコンストラクターを呼び出すとされています。
ReplyDelete