C++のコピーエリージョン(Copy Elision)とは?仕組みをサンプルコードでわかりやすく解説
コピーエリージョン(Copy Elision)は「コピーの省略(Copy Omission)」とも呼ばれる、C++における代表的なコンパイラ最適化技術のひとつです。この技術により、オブジェクトの不要なコピー処理が自動的に回避されます。現在利用されているほぼすべてのコンパイラが、このコピーエリージョンを採用しています。
ここからは、実際のサンプルコードを使いながら、その仕組みを詳しく見ていきましょう。
サンプルコード
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass(const char* str = "\0") { //デフォルトコンストラクタ
cout << "Constructor called" << endl;
}
MyClass(const MyClass &my_cls) { //コピーコンストラクタ
cout << "Copy constructor called" << endl;
}
};
int main() {
MyClass ob = "copy class object";
}
実行結果
Constructor called
なぜコピーコンストラクタが呼び出されないのか?
実行結果を見ると、「Copy constructor called」は一切出力されておらず、コピーコンストラクタが一度も呼び出されていないことが分かります。これには明確な理由があります。
本来、オブジェクトが構築される際には、まず一時オブジェクト(テンポラリオブジェクト)が生成され、それが実際のオブジェクトへコピーされるという流れになります。つまり、内部的には次のようなコードが実行されていると考えられます。
MyClass ob = "copy class object";
この記述は、概念的には以下のように動作します。
MyClass ob = MyClass("copy class object");
しかし、一時オブジェクトを生成してコピーする这样的処理は、パフォーマンス上の無駄なオーバーヘッドとなります。そこでC++コンパイラは、この余分なコピーを検出し、一時オブジェクトを経由せずに目的のオブジェクトを直接構築するよう最適化を行います。これこそが、コピーエリージョンの正体です。
補足:C++17以降の「保証されたコピー省略」
C++17以降では、prvalue(純右辺値)による初期化など一部のケースにおいて、コピーエリージョンが言語仕様として義務付けられています。これにより、コピーやムーブコンストラクタが削除(delete指定)されたクラスであっても、該当する場面では問題なくオブジェクトを返却・構築できるようになりました。
一方で、コピー・ムーブコンストラクタ内にログ出力などの副作用を含んでいる場合、省略によってそれらの処理が実行されない可能性がある点には注意が必要です。
-
C++でkを超えずに実行できるコピー操作の最大回数を求める方法
問題概要 n個の要素を持つ配列Aと整数kが与えられます。n個のキャンディの山があり、i番目の山にはA[i]個のキャンディが入っています。ここで、2つの異なるインデックスiとj(i != j)を選んで操作を行うと、A[i]個のキャンディを別の山へ追加できます(コピー元の山は減りません)。この操作は何度でも繰り返せますが、どれか一つの山でもキャンディ数がkを厳密に超えると、それ以上操作は行えません。このとき、操作を実行できる最大回数を求めるのが目的です。 例えば、入力が A = [1, 2, 3]、k = 5 の場合、出力は 5 になります。最小の山(A[0] = 1)をコピー元として使い、j =
-
C++でランダムポインタを持つリンクリストをディープコピーする方法
ランダムポインタを持つリンクリストとはリンクリスト(連結リスト)は代表的な線形データ構造の一つで、各ノードは「ノードが保持する値(データ)」と「次のノードのアドレスを格納するポインタ(next)」という2つの部分で構成されます。本記事では、さらに各ノードがリスト内の別のノードを指す「ランダムポインタ(random)」を持つリンクリストを扱います。このようなリストに対して、元のリストと同じデータ・同じランダムポインタ構造を持つ新しいリストを作成することを、リンクリストの「ディープコピー(Deep Copy)」と呼びます。例入力:出力:5-> 2 -> 3 -> 7 ->4