質問: base」の初期化が行われているはずなので、コピーコンストラクタが呼ばれると思うのですが、呼ばれていません。 ここで、CBaseのコピーコンストラクタをprivateにして呼び出し禁止にすると、コンパイルエラーが発生します。 これは、コピーコンストラクタを呼び出す建前で構文はチェックされるが、最適化でコピーコンストラクタが呼ばれない形でオブジェクトが返されているということなのでしょうか。 ここから、関連した質問です。 上記の仮説が正しいとすると、最適化が行われない場合、以下のコードは戻り値のテンポラリオブジェクトをテンポラリオブジェクトで初期化して、それを返していることになり、2つ分のデストラクタが呼ばれることになるのでしょうか。 ``` CBase Create() { return CBase(2); } ``` また、Create関数で定義したCBase base(2);は、ローカル変数なので、Create関数を抜けたとき(operator=が呼ばれる前)に、デストラクタが呼ばれると思うのですが、なぜ呼ばれないのでしょうか。 (予想としては、戻り値のテンポラリオブジェクトをbaseで初期化せず、baseをテンポラリオブジェクトとして直接返しているから?) 以上、宜しくお願い致します。
回答: こんにちは。 > > しかしそれならば、「CBase テンポラリオブジェクト = base」の初期化が行われているはずなので、コピーコンストラクタが呼ばれると思うのですが、呼ばれていません。 > > > ですね。文法上は呼ばれるはずですが、実際には呼ばれていないです。 これは、頭の良いコンパイラが対応しているNRVO(Named Return Value Optimization)と呼ばれる最適化機能が働いたということのようです。 ここの説明が分かりやすいです。 ちょっと古いですが、ここが規格書の該当箇所を説明しています。 > > これは、コピーコンストラクタを呼び出す建前で構文はチェックされるが、最適化でコピーコンストラクタが呼ばれない形でオブジェクトが返されているということなのでしょうか。 > > > が正解ということですね。 > > 最適化が行われない場合、以下のコードは戻り値のテンポラリオブジェクトをテンポラリオブジェクトで初期化して、それを返していることになり、2つ分のデストラクタが呼ばれることになるのでしょうか。 > > > 最適化されない場合は、main()のbase(1)とCreate()のbase(2)と一時オブジェクトで、合計3つ分のデストラクタが呼ばれるのではないかと思います。 ただ、こちらの場合は、名前無しオブジェクトを返却しているのでRVO(Return Value Optimization)と呼ばれる最適化の範疇で、これはNRVOより頭の悪いコンパイラでも対応できるらしいです。 上記のページが、「いやしくも C++ コンパイラを名乗るからには、 必須の機能である。」と書く程、多くのコンパイラが対応しているのでしょう。 > > また、Create関数で定義したCBase base(2);は、ローカル変数なので、Create関数を抜けたとき(operator=が呼ばれる前)に、デストラクタが呼ばれると思うのですが、なぜ呼ばれないのでしょうか。 > > > NVROが働くということはbase(2)は返却用の一時オブジェクト上に直接コンストラクトされるということなので、Create()からリターンしてもデストラクトされないのだろうと思います。つまり、一見ローカル変数ですが、NVROにより一時オブジェクト領域にコンストラクトされていると言うことですね。 いや~、C++コンパイラって本当に頭いいですね。