Copy assignment operator
コピー代入演算子は、同じクラス型の引数で呼び出すことができ、引数を変更せずにその内容をコピーする、 operator = という名前の非テンプレート 非静的メンバ関数 です。
目次 |
構文
正式なコピー代入演算子の構文については、 function declaration を参照してください。以下の構文リストは、有効なコピー代入演算子の構文の一部のみを示しています。
戻り値型
operator=(
パラメータリスト
);
|
(1) | ||||||||
戻り値型
operator=(
パラメータリスト
)
関数本体
|
(2) | ||||||||
戻り値型
operator=(
デフォルト非対応パラメータリスト
) = default;
|
(3) | (C++11以降) | |||||||
戻り値型
operator=(
パラメータリスト
) = delete;
|
(4) | (C++11以降) | |||||||
戻り値型
クラス名
::
operator=(
パラメータリスト
)
関数本体
|
(5) | ||||||||
戻り値型
クラス名
::
operator=(
デフォルト非対応パラメータリスト
) = default;
|
(6) | (C++11以降) | |||||||
| class-name | - |
コピー代入演算子が宣言されているクラス。以下の説明ではクラス型は
T
として示される
|
| parameter-list | - |
パラメータリスト
で、型が
T
、
T&
、
const
T
&
、
volatile
T
&
または
const
volatile
T
&
の単一パラメータのみからなる
|
| parameter-list-no-default | - |
パラメータリスト
で、型が
T
、
T&
、
const
T
&
、
volatile
T
&
または
const
volatile
T
&
の単一パラメータのみからなり、デフォルト引数を持たない
|
| function-body | - | コピー代入演算子の 関数本体 |
| return-type | - |
任意の型、ただし代入の連鎖を可能にするために
T&
が推奨される
|
説明
struct X { X& operator=(X& other); // コピー代入演算子 X operator=(X other); // 値渡しは許可される // X operator=(const X other); // エラー: 不正なパラメータ型 }; union Y { // コピー代入演算子は上記に列挙されていない構文を持つことができます // 一般的な関数宣言の構文に従い // 上記に記載された制限に違反しない限り auto operator=(Y& other) -> Y&; // OK: 後置戻り値型 Y& operator=(this Y& self, Y& other); // OK: 明示的オブジェクトパラメータ // Y& operator=(Y&, int num = 1); // エラー: 他の非オブジェクトパラメータを持つ };
コピー代入演算子は、 オーバーロード解決 によって選択されるたびに呼び出されます。例えば、オブジェクトが代入式の左辺に現れる場合などです。
暗黙的に宣言されるコピー代入演算子
クラス型に対してユーザー定義のコピー代入演算子が提供されない場合、コンパイラは常にそれを inline public メンバーとして宣言します。この暗黙的に宣言されるコピー代入演算子は、以下のすべての条件が満たされる場合に T & T :: operator = ( const T & ) の形式を取ります:
-
Tの各直接基底クラス
Bが、パラメータがBまたは const B & または const volatile B & であるコピー代入演算子を持つこと; -
Tのクラス型またはクラス型の配列である各非静的データメンバー
Mが、パラメータがMまたは const M & または const volatile M & であるコピー代入演算子を持つこと。
それ以外の場合、暗黙的に宣言されるコピー代入演算子は T & T :: operator = ( T & ) として宣言されます。
これらの規則により、暗黙的に宣言されたコピー代入演算子は volatile 左辺値引数にバインドできません。
クラスは複数のコピー代入演算子を持つことができます。例えば、 T & T :: operator = ( T & ) と T & T :: operator = ( T ) の両方です。 ユーザー定義のコピー代入演算子が存在する場合でも、ユーザーはキーワード default を使用して暗黙的に宣言されたコピー代入演算子の生成を強制できます。 (C++11以降)
暗黙的に宣言された(または最初の宣言でデフォルト化された)コピー代入演算子は、以下のように例外仕様を持ちます: 動的例外仕様 (C++17まで) noexcept仕様 (C++17以降)
コピー代入演算子はあらゆるクラスに対して常に宣言されるため、基底クラスの代入演算子は常に隠蔽されます。 using-declaration を使用して基底クラスから代入演算子を取り込む場合、その引数型が派生クラスの暗黙的な代入演算子の引数型と同じである可能性があるとき、using-declarationも暗黙的な宣言によって隠蔽されます。
暗黙的に定義されるコピー代入演算子
暗黙的に宣言されたコピー代入演算子が削除もトリビアルもされない場合、それが ODR-used または 定数評価に必要とされる (C++14以降) 場合、コンパイラによって定義される(つまり、関数本体が生成されコンパイルされる)。共用体型の場合、暗黙的に定義されたコピー代入はオブジェクト表現をコピーする( std::memmove によるように)。非共用体クラス型の場合、この演算子はオブジェクトの直接基底クラスと非静的データメンバに対して、初期化順序で、スカラー型には組み込みの代入を、配列にはメンバ単位のコピー代入を、クラス型にはコピー代入演算子を(非仮想的に呼び出して)実行する。
|
クラス
|
(C++14 以降)
(C++23 まで) |
|
クラス
|
(C++23 以降) |
|
|
(C++11以降) |
削除されたコピー代入演算子
暗黙的に宣言される
または明示的にデフォルト化された
(C++11以降)
クラス
T
のコピー代入演算子は、以下の条件のいずれかを満たす場合に
未定義
(C++11以前)
削除定義される
(C++11以降)
:
-
Tが const 修飾された非クラス型(または多次元配列)の非静的データメンバを持つ場合 -
Tが参照型の非静的データメンバを持つ場合 -
Tがクラス型M(または多次元配列)の 潜在的に構築される部分オブジェクト を持ち、かつMのコピー代入演算子を探索する際のオーバーロード解決において
-
- 使用可能な候補が得られない場合、または
- サブオブジェクトが variant member である場合に、非自明な関数を選択する場合。
|
クラス
|
(C++11以降) |
自明なコピー代入演算子
T
クラスのコピー代入演算子は、以下の条件がすべて真である場合に自明となります:
- ユーザー提供ではない(つまり、暗黙的に定義されるかデフォルト化されている)こと;
-
Tが仮想メンバ関数を持たないこと; -
Tが仮想基底クラスを持たないこと; -
Tのすべての直接基底クラスに対して選択されるコピー代入演算子がトリビアルであること; -
Tのすべての非静的クラス型(またはクラス型の配列)メンバに対して選択されるコピー代入演算子がトリビアルであること。
自明なコピー代入演算子は、オブジェクト表現を std::memmove によってコピーするかのように複製します。C言語と互換性のあるすべてのデータ型(POD型)は自明にコピー代入可能です。
有効なコピー代入演算子
|
コピー代入演算子は、ユーザー宣言されているか、暗黙的に宣言されかつ定義可能である場合に適格となる。 |
(C++11まで) |
|
コピー代入演算子は、削除されていない場合に適格となる。 |
(C++11から)
(C++20まで) |
|
コピー代入演算子は、以下の条件をすべて満たす場合に適格となる: |
(C++20から) |
有効なコピー代入演算子の自明性は、クラスが 自明コピー可能型 かどうかを決定します。
注記
コピー代入演算子とムーブ代入演算子の両方が提供されている場合、引数が rvalue (名前のない一時オブジェクトなどの prvalue や xvalue ( std::move の結果など))の場合、オーバーロード解決はムーブ代入を選択し、引数が lvalue (名前付きオブジェクトや左辺値参照を返す関数/演算子)の場合、コピー代入を選択します。コピー代入のみが提供されている場合、すべての引数カテゴリがそれを選択します(rvalueはconst参照にバインドできるため、引数を値またはconst参照として取る限り)。これにより、ムーブが利用できない場合、コピー代入はムーブ代入の代替として機能します。
仮想基底クラスのサブオブジェクトが継承階層内で複数の経路を通じてアクセス可能である場合、それらが暗黙的に定義されたコピー代入演算子によって複数回代入されるかどうかは未規定である(これは move assignment にも同様に適用される)。
詳細については、ユーザー定義のコピー代入演算子の期待される動作について 代入演算子のオーバーロード を参照してください。
例
#include <algorithm> #include <iostream> #include <memory> #include <string> struct A { int n; std::string s1; A() = default; A(A const&) = default; // ユーザー定義のコピー代入(コピー・アンド・スワップイディオム) A& operator=(A other) { std::cout << "copy assignment of A\n"; std::swap(n, other.n); std::swap(s1, other.s1); return *this; } }; struct B : A { std::string s2; // 暗黙的に定義されるコピー代入 }; struct C { std::unique_ptr<int[]> data; std::size_t size; // ユーザー定義のコピー代入(非コピー・アンド・スワップイディオム) // 注意: コピー・アンド・スワップは常にリソースの再割り当てを行う C& operator=(const C& other) { if (this != &other) // 自己代入ではない場合 { if (size != other.size) // リソースを再利用できない場合 { data.reset(new int[other.size]); size = other.size; } std::copy(&other.data[0], &other.data[0] + size, &data[0]); } return *this; } }; int main() { A a1, a2; std::cout << "a1 = a2 calls "; a1 = a2; // ユーザー定義のコピー代入 B b1, b2; b2.s1 = "foo"; b2.s2 = "bar"; std::cout << "b1 = b2 calls "; b1 = b2; // 暗黙的に定義されるコピー代入 std::cout << "b1.s1 = " << b1.s1 << "; b1.s2 = " << b1.s2 << '\n'; }
出力:
a1 = a2 calls copy assignment of A b1 = b2 calls copy assignment of A b1.s1 = foo; b1.s2 = bar
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用バージョン | 公開時の仕様 | 修正後の仕様 |
|---|---|---|---|
| CWG 1353 | C++98 |
暗黙宣言されるコピー代入演算子が未定義となる条件に
多次元配列型が考慮されていなかった |
これらの型を考慮する |
| CWG 2094 | C++11 |
volatile サブオブジェクトがある場合、デフォルト化されたコピー
代入演算子が非自明になっていた( CWG issue 496 ) |
自明性に影響しない |
| CWG 2171 | C++11 | operator = ( X & ) = default が非自明だった | 自明化された |
| CWG 2180 | C++11 |
クラス
T
が抽象クラスであり、コピー代入不可能な直接仮想基底クラスを
持つ場合、
T
のデフォルト化されたコピー代入演算子が削除定義されなかった
|
この場合に演算子は
削除定義される |
| CWG 2595 | C++20 |
より制約されているが関連制約を満たさない別のコピー代入演算子が
存在する場合、コピー代入演算子が適格ではなかった |
この場合でも
適格となりうる |