したがって、右辺値参照に関するこの素晴らしい講義を見た後、Philippが動的に割り当てた場合、Philippが回答で指摘しているように、すべてのクラスがそのような「ムーブコンストラクター」、template<class T> MyClass(T&& other)
編集、そしてもちろん「ムーブ代入演算子」の恩恵を受けると思いました。template<class T> MyClass& operator=(T&& other)
メンバー、または一般的にポインタを格納します。前述のポイントが当てはまる場合は、コピーコンストラクタ、代入演算子、およびデストラクタが必要です。考え?
9 に答える
三つのルールは三つのルール、四、五のルールになると思います。
各クラスは、次の特殊メンバー関数のセットの1つを明示的に定義する必要があります。
- なし
- デストラクタ、コピーコンストラクタ、コピー代入演算子
さらに、デストラクタを明示的に定義する各クラスは、ムーブコンストラクタおよび/またはムーブ代入演算子を明示的に定義できます。
通常、次の特別なメンバー関数のセットの1つが賢明です。
- なし(暗黙的に生成された特殊メンバー関数が正しく高速である多くの単純なクラスの場合)
- デストラクタ、コピーコンストラクタ、コピー代入演算子(この場合、クラスは移動できません)
- デストラクタ、ムーブコンストラクタ、ムーブ代入演算子(この場合、クラスはコピーできません。基になるリソースがコピーできないリソース管理クラスに役立ちます)
- デストラクタ、コピーコンストラクタ、コピー代入演算子、移動コンストラクタ(コピーの省略のため、コピー代入演算子が値によって引数を取る場合、オーバーヘッドはありません)
- デストラクタ、コピーコンストラクタ、コピー代入演算子、ムーブコンストラクタ、ムーブ代入演算子
ノート:
- そのムーブコンストラクタとムーブ代入演算子は、他の特別なメンバー関数(デストラクタ、コピーコンストラクタ、ムーブ代入演算子など)を明示的に宣言するクラスに対しては生成されません。
- そのコピーコンストラクターとコピー代入演算子は、ムーブコンストラクターまたはムーブ代入演算子を明示的に宣言するクラスに対しては生成されません。
- また、明示的に宣言されたデストラクタと暗黙的に定義されたコピーコンストラクタまたは暗黙的に定義されたコピー代入演算子を持つクラスは非推奨と見なされます。
特に、次の完全に有効なC ++03ポリモーフィック基本クラス:
class C {
virtual ~C() { } // allow subtype polymorphism
};
次のように書き直す必要があります。
class C {
C(const C&) = default; // Copy constructor
C(C&&) = default; // Move constructor
C& operator=(const C&) = default; // Copy assignment operator
C& operator=(C&&) = default; // Move assignment operator
virtual ~C() { } // Destructor
};
少し面倒ですが、おそらく他の方法よりも優れています(この場合、移動の可能性なしに、コピーのみの特別なメンバー関数の自動生成)。
ルールに従わないと深刻な損害を引き起こす可能性があるビッグスリーのルールとは対照的に、ムーブコンストラクターとムーブ代入演算子を明示的に宣言しないことは一般に問題ありませんが、効率に関しては最適ではないことがよくあります。上記のように、ムーブコンストラクタとムーブ代入演算子は、明示的に宣言されたコピーコンストラクタ、コピー代入演算子、またはデストラクタがない場合にのみ生成されます。これは、コピーコンストラクターとコピー代入演算子の自動生成に関して従来のC ++ 03の動作と対称的ではありませんが、はるかに安全です。したがって、ムーブコンストラクターとムーブ代入演算子を定義する可能性は非常に便利であり、新しい可能性(純粋に移動可能なクラス)を作成しますが、ビッグスリーのC++03ルールに準拠するクラスは引き続き問題ありません。
リソース管理クラスの場合、基になるリソースをコピーできない場合は、コピーコンストラクターとコピー代入演算子を削除済み(定義としてカウント)として定義できます。多くの場合、ムーブコンストラクターとムーブ代入演算子が必要です。コピーおよびムーブ代入演算子はswap
、C ++ 03のように、を使用して実装されることがよくあります。について話すswap
; すでにムーブコンストラクターとムーブ代入演算子がある場合、ジェネリックは可能な場合はムーブコンストラクターとムーブ代入演算子を使用するため、特殊化はstd::swap
重要ではなくなりstd::swap
ます(十分に高速である必要があります)。
リソース管理(つまり、空でないデストラクタがない)またはサブタイプポリモーフィズム(つまり、仮想デストラクタがない)を対象としていないクラスは、5つの特別なメンバー関数のいずれも宣言しないでください。それらはすべて自動生成され、正しく高速に動作します。
誰もこれにリンクしていないなんて信じられない。
基本的に記事は「ゼロのルール」について論じています。記事全体を引用するのは適切ではありませんが、これが要点だと思います。
カスタムデストラクタ、コピー/ムーブコンストラクタ、またはコピー/ムーブ代入演算子を持つクラスは、所有権のみを処理する必要があります。他のクラスには、カスタムデストラクタ、コピー/ムーブコンストラクタ、またはコピー/ムーブ代入演算子を含めないでください。
また、このビットは私見重要です:
一般的な「パッケージ内の所有権」クラスは、標準ライブラリに含まれています:
std::unique_ptr
およびstd::shared_ptr
。カスタム削除オブジェクトを使用することで、どちらも事実上あらゆる種類のリソースを管理するのに十分な柔軟性を備えています。
私はそうは思いません。三つのルールは、次のいずれかを実装しているが、すべてではないクラスはおそらくバグがあると述べている経験則です。
- コピーコンストラクタ
- 代入演算子
- デストラクタ
ただし、ムーブコンストラクターまたはムーブ代入演算子を省略しても、バグを意味するものではありません。最適化の機会を逃したか(ほとんどの場合)、移動セマンティクスがこのクラスに関連していない可能性がありますが、これはバグではありません。
関連する場合は移動コンストラクターを定義するのがベストプラクティスかもしれませんが、必須ではありません。移動コンストラクターがクラスに関連しない場合が多く(例std::complex
)、C ++ 03で正しく動作するすべてのクラスは、移動コンストラクターを定義していなくても、C++0xで引き続き正しく動作します。 。
はい、そのようなクラスにmoveコンストラクターを提供するのは良いことだと思いますが、次のことを覚えておいてください。
これは単なる最適化です。
コピーコンストラクタ、代入演算子、またはデストラクタの1つまたは2つだけを実装すると、バグが発生する可能性がありますが、移動コンストラクタがないと、パフォーマンスが低下する可能性があります。
移動コンストラクターは、変更せずに常に適用できるとは限りません。
一部のクラスには常にポインタが割り当てられているため、そのようなクラスは常にデストラクタ内のポインタを削除します。このような場合、ポインタが割り当てられているか、移動されているか(現在はnull)を確認するためのチェックを追加する必要があります。
これは、2011年1月24日以降の現在の状況と関連する開発に関する短い更新です。
C ++ 11標準によると(付録Dの[depr.impldec]を参照):
クラスにユーザー宣言のコピー代入演算子またはユーザー宣言のデストラクタがある場合、コピーコンストラクタの暗黙的な宣言は非推奨になります。クラスにユーザー宣言のコピーコンストラクターまたはユーザー宣言のデストラクタがある場合、コピー代入演算子の暗黙的な宣言は非推奨になります。
実際には、廃止された動作を廃止して、C++14に従来の「三つのルール」ではなく真の「三つのルール」を与えることが提案されました。2013年、EWGは、C++2014で実装されるこの提案に反対票を投じました。提案に関する決定の主な理由は、既存のコードを破ることに関する一般的な懸念と関係がありました。
最近、非公式の5つのルールを達成するために、C++11の文言を適応させることが再び提案されました。
これらの関数のいずれかがユーザー提供である場合、コピー関数、移動関数、またはデストラクタはコンパイラーによって生成されません。
EWGによって承認された場合、「ルール」はC++17に採用される可能性があります。
基本的には次のようになります。移動操作を宣言しない場合は、3のルールを尊重する必要があります。移動操作を宣言する場合、コンパイラーによって生成される操作の生成は非常に制限されているため、3つのルールに「違反」しても害はありません。移動操作を宣言せず、3つのルールに違反している場合でも、C ++ 0xコンパイラは、1つの特殊関数がユーザー宣言され、他の特殊関数が次の理由で自動生成された場合に警告を出すことが期待されます。 「C++03互換性ルール」は非推奨になりました。
このルールは少し重要性が低くなると言っても過言ではありません。C ++ 03の本当の問題は、異なるコピーセマンティクスを実装するには、関連するすべての特殊関数をユーザー宣言して、コンパイラによって生成されないようにする必要があることです(そうしないと間違ったことをします)。ただし、C ++ 0xは、特殊メンバー関数の生成に関する規則を変更します。ユーザーがこれらの関数の1つだけを宣言してコピーのセマンティクスを変更すると、コンパイラーは残りの特殊関数を自動生成できなくなります。宣言が欠落していると、実行時エラーがコンパイルエラー(または少なくとも警告)に変わるため、これは適切です。C ++ 03の互換性対策として、一部の操作は引き続き生成されますが、この生成は非推奨と見なされ、少なくともC++0xモードで警告を生成する必要があります。
コンパイラによって生成された特殊関数とC++03の互換性に関するかなり制限されたルールにより、3のルールは3のルールのままです。
最新のC++0xルールで問題ないはずの例を次に示します。
template<class T>
class unique_ptr
{
T* ptr;
public:
explicit unique_ptr(T* p=0) : ptr(p) {}
~unique_ptr();
unique_ptr(unique_ptr&&);
unique_ptr& operator=(unique_ptr&&);
};
上記の例では、他の特殊関数を削除済みとして宣言する必要はありません。制限的なルールのため、これらは単に生成されません。ユーザーが宣言した移動操作が存在すると、コンパイラーが生成したコピー操作が無効になります。しかし、このような場合:
template<class T>
class scoped_ptr
{
T* ptr;
public:
explicit scoped_ptr(T* p=0) : ptr(p) {}
~scoped_ptr();
};
C ++ 0xコンパイラは、間違ったことを行う可能性のあるコンパイラ生成のコピー操作に関する警告を生成することが期待されています。ここでは、三つのルールが重要であり、尊重されるべきです。この場合の警告は完全に適切であり、ユーザーにバグを処理する機会を与えます。削除された関数を介して問題を取り除くことができます:
template<class T>
class scoped_ptr
{
T* ptr;
public:
explicit scoped_ptr(T* p=0) : ptr(p) {}
~scoped_ptr();
scoped_ptr(scoped_ptr const&) = delete;
scoped_ptr& operator=(scoped_ptr const&) = delete;
};
したがって、C ++ 03との互換性があるという理由だけで、ここでも3のルールが適用されます。
三つのルールを強制し、移動セマンティクスの形式を実装しない既存のコードをすべて壊さずに、三つのルールが4(または5)のルールになるとは言えません。
三つのルールは、1つを実装する場合、3つすべてを実装する必要があることを意味します。
また、自動生成された動きがあることに気づいていません。「三つのルール」の目的は、それらが自動的に存在するためであり、1つを実装すると、他の2つのデフォルトの実装が間違っている可能性があります。
一般的なケースでは、そうです。ムーブ代入演算子とムーブコンストラクターが追加されて、3のルールが5のルールになりました。ただし、すべてのクラスがコピー可能で移動可能であるとは限りません。一部は移動可能で、一部はコピー可能です。
簡単に言えば、これを覚えておいてください。
0のルール:
Classes have neither custom destructors, copy/move constructors or copy/move assignment operators.
三つのルール:これらのいずれかのカスタムバージョンを実装する場合は、それらすべてを実装します。
Destructor, Copy constructor, copy assignment
ルール5:カスタムの移動コンストラクターまたは移動割り当て演算子を実装する場合は、5つすべてを定義する必要があります。移動セマンティクスに必要です。
Destructor, Copy constructor, copy assignment, move constructor, move assignment
4年半のルール:5のルールと同じですが、コピーとスワップのイディオムがあります。スワップメソッドを含めると、コピー代入とムーブ代入が1つの代入演算子にマージされます。
Destructor, Copy constructor, move constructor, assignment, swap (the half part)
参照:
https://www.linkedin.com/learning/c-plus-plus-advanced-topics/rule-of-five?u=67551194 https://en.cppreference.com/w/cpp/language/rule_of_three