コピー代入演算子
クラス T のコピー代入演算子は、 T、 T&、 const T&、 volatile T& または const volatile T& 型の引数をちょうど1個取る operator= という名前の非テンプレート非静的メンバ関数です。 型が CopyAssignable であるためには、パブリックなコピー代入演算子を持たなければなりません。
構文
class_name & class_name :: operator= ( class_name )
|
(1) | ||||||||
class_name & class_name :: operator= ( const class_name & )
|
(2) | ||||||||
class_name & class_name :: operator= ( const class_name & ) = default;
|
(3) | (C++11以上) | |||||||
class_name & class_name :: operator= ( const class_name & ) = delete;
|
(4) | (C++11以上) | |||||||
説明
- コピーアンドスワップイディオムが使用できるときの一般的なコピー代入演算子の宣言。
- コピーアンドスワップイディオムが使用できない (型がスワップ可能でないまたは性能的に不利) ときの一般的なコピー代入演算子の宣言。
- コピー代入演算子を強制的にコンパイラに生成させます。
- 暗黙のコピー代入を回避します。
コピー代入演算子は、オブジェクトが代入式の左側に現れたときなどに、オーバーロード解決によって選択されたときに、呼ばれます。
暗黙に宣言されたコピー代入演算子
クラス型 (struct、 class または union) に対してユーザ定義されたコピー代入演算子が提供されない場合、コンパイラはそのクラスのインラインパブリックメンバとしてそれを必ず宣言します。 この暗黙に宣言されたコピー代入演算子は、以下のすべてが真の場合、 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=(const T&) と T& T::operator=(T) の両方を持つことができます。 何らかのユーザ定義されたコピー代入演算子が存在する場合でも、ユーザはキーワード default を用いて暗黙に宣言されたコピー代入演算子の生成を強制できます。 (C++11以上)
暗黙に宣言された (またはその最初の宣言においてデフォルト化された) コピー代入演算子は、動的例外指定 (C++17未満)例外指定 (C++17以上)で説明されている通りの例外指定を持ちます。
コピー代入演算子はいかなるクラスに対しても必ず宣言されるため、基底クラスの代入演算子は必ず隠蔽されます。 基底クラスの代入演算子を取り込むために using 宣言が使用され、その引数の型が派生クラスの暗黙の代入演算子の引数の型と同じである場合は、その using 宣言も暗黙の宣言によって隠蔽されます。
削除された暗黙に宣言されたコピー代入演算子
クラス T に対する暗黙に宣言されたコピー代入演算子は、以下のいずれかが真の場合、削除されたものとして定義されます。
Tがユーザ宣言されたムーブコンストラクタを持つ。Tはユーザ宣言されたムーブ代入演算子を持つ。
そうでなければ、デフォルト化されたものとして定義されます。
クラス T に対するデフォルト化されたコピー代入演算子は、以下のいずれかが真の場合、削除されたものとして定義されます。
Tがconstである非クラス型 (またはその配列型) の非静的データメンバを持つ。Tが参照型の非静的データメンバを持つ。Tがコピー代入できない (コピー代入に対するオーバーロード解決が失敗する、または削除されたまたはアクセス不可能な関数を選択する) 非静的データメンバまたは直接または仮想の基底クラスを持つ。Tが union ライクなクラスであり、対応する代入演算子が非トリビアルである変種メンバを持つ。
トリビアルなコピー代入演算子
以下のすべてが真の場合、クラス T に対するコピー代入演算子はトリビアルです。
- ユーザ定義されていない (つまり、暗黙に定義されている、またはデフォルト化されている)。 また、デフォルト化されている場合は、そのシグネチャが暗黙に定義されたものと同じである。 (C++14未満)
Tが仮想メンバ関数を持たない。Tが仮想基底クラスを持たない。Tのすべての直接の基底について、選択されたコピー代入演算子がトリビアルである。Tのすべてのクラス型 (またはクラスの配列型) の非静的メンバについて、選択されたコピー代入演算子がトリビアルである。
|
(C++14以上) |
トリビアルなコピー代入演算子はオブジェクト表現のコピーを std::memmove によって行われたかのように行います。 C 言語と互換性のあるすべてのデータ型 (POD 型) はトリビアルにコピー代入可能です。
暗黙に定義されたコピー代入演算子
暗黙に宣言されたコピー代入演算子が削除されておらずトリビアルでもない場合は、 ODR 使用された場合、コンパイラによって定義されます (つまり、関数の本体が生成され、コンパイルされます)。 union 型の場合、暗黙に定義されたコピー代入演算子は、オブジェクト表現を (std::memmove によって行われたかのように) コピーします。 非 union クラス型 (class および struct) の場合、暗黙に定義されたコピー代入演算子は、オブジェクトの基底および非静的メンバのメンバ単位のコピー代入を、その初期化の順で、スカラー型については組み込みの代入をクラス型についてはコピー代入演算子を用いて、行います。
T がユーザ宣言されたデストラクタまたはユーザ宣言されたコピーコンストラクタを持つ場合、暗黙に定義されたコピー代入演算子の生成は非推奨です(C++11以上)。
ノート
コピーとムーブ両方の代入演算子が提供されている場合、オーバーロード解決は引数が右辺値 (名前のない一時オブジェクトなどの prvalue または std::move の結果などの xvalue のいずれか) であればムーブ代入を選択し、引数が左辺値 (名前付きのオブジェクトまたは左辺値参照を返す関数/演算子) であればコピー代入を選択します。 コピー代入のみが提供されている場合は、すべての引数カテゴリがそれを選択します (引数を値で取るか const への参照として取る限り (右辺値は const 参照に束縛できるため))。 これは、ムーブが利用可能でないときに、コピー代入をムーブ代入のフォールバックにします。
継承階層内の複数の経路でアクセス可能な仮想基底クラスの部分オブジェクトが、暗黙に定義されたコピー代入演算子によって複数回代入されるかどうかは、未規定です (同じことがムーブ代入にも適用されます)。
ユーザ定義されたコピー代入演算子の期待される動作のさらなる詳細については代入演算子のオーバーロードを参照してください。
例
#include <iostream>
#include <memory>
#include <string>
#include <algorithm>
struct A
{
int n;
std::string s1;
// ユーザ定義されたコピー代入 (コピーアンドスワップ形式)。
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(&other == this)
return *this;
// 可能なときは記憶域を再利用します。
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 2171 | C++14 | operator=(X&) = default was non-trivial
|
made trivial |