web-dev-qa-db-ja.com

C ++ 11で明示的に削除されたメンバー関数では、コピー不可の基本クラスから継承することはまだ価値がありますか?

C++ 11で明示的に削除されたメンバー関数では、コピー不可の基本クラスから継承することはまだ価値がありますか?

私は、プライベートまたは削除されたコピーコンストラクタとコピー割り当てを持つベースクラスをプライベートに継承するトリックについて話している(例えば boost::noncopyable )。

この点で利点が提起されています question はまだC++ 11に適用可能ですか?


一部の人々がC++ 11でクラスをコピー不可にする方が簡単だと主張する理由がわかりません。

C++ 03の場合:

private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}

C++ 11の場合:

MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;

編集:

多くの人が指摘しているように、プライベートコピーコンストラクターとコピー割り当て演算子に空のボディ(つまり{})を提供するのは間違いでした。クラス自体がエラーなしでそれらの演算子を呼び出すことができるからです。最初は{}を追加しないことから始めましたが、何らかの愚かな理由で{}を追加するリンカーの問題に遭遇しました(状況を覚えていません)。私はよく知っています。 :-)

58
Emile Cormier

さて、これ:

private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}

技術的には、MyClassをメンバーや友人がコピーすることを許可しています。確かに、これらのタイプと関数は理論的にはあなたの制御下にありますが、クラスはまだcopyableです。少なくともboost::noncopyableおよび= deletenobodyはクラスをコピーできます。


一部の人々がC++ 11でクラスをコピー不可にする方が簡単だと主張する理由がわかりません。

「簡単に消化できる」ほど「簡単」ではありません。

このことを考慮:

class MyClass
{
private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}
};

あなたがC++の入門テキストを読んでいるが、慣用的なC++にほとんど触れていないC++プログラマー(つまり:C++プログラマーのlot)の場合、これは...紛らわしいです。コピーコンストラクターとコピー代入演算子を宣言しますが、空です。では、なぜそれらを宣言するのでしょうか?はい、それらはprivateですが、それはmoreの質問を発生させるだけです:なぜそれらをプライベートにするのですか?

これがコピーを妨げる理由を理解するには、それらをプライベートと宣言することにより、非メンバー/友人がコピーできないようにすることを理解する必要があります。これは初心者にはすぐにはわかりません。また、コピーしようとしたときに表示されるエラーメッセージもありません。

次に、C++ 11バージョンと比較します。

class MyClass
{
public:
    MyClass(const MyClass&) = delete;
    MyClass& operator=(const MyClass&) = delete;
};

このクラスをコピーできないことを理解するには何が必要ですか? = delete構文は意味します。 C++ 11の構文規則を説明する本は、それが何をするかを正確に教えてくれます。このコードの効果は、経験の浅いC++ユーザーには明らかです。

このイディオムの素晴らしいところは、それがイディオムになるということです。なぜなら、それがあなたの言っていることを正確に言う最も明確で最も明白な方法だからです。

boost::noncopyableはもう少し考えが必要です。はい、「コピー不可」と呼ばれるため、自己文書化されます。しかし、それを見たことがない場合は、疑問が生じます。コピーできないものから派生しているのはなぜですか?エラーメッセージにboost::noncopyableのコピーコンストラクタ?繰り返しますが、イディオムを理解するにはより多くの精神的な努力が必要です。

75
Nicol Bolas

最初のことは、私の前に指摘したように、あなたはイディオムを間違えた、あなたはdeclare privateであり、define

class noncopyable {
   noncopyable( noncopyable const & );
   noncopyable& operator=( noncopyable const & );
};

operator=の戻り値の型は基本的に何でもかまいません。この時点で、ヘッダーでそのコードを読んだ場合、実際にはどういう意味ですか?友人だけがコピーできますか、それともコピーできませんか?例のように定義を提供する場合、クラス内および友人のみがコピーできると記述しているため、これがに変換されるのは定義の欠如です。コピーできません。ただし、ヘッダーに定義がないことは、cppファイルで定義できるため、定義がないeverywhereの同義語ではありません。

noncopyableという型から継承すると、上記のコードを手動で記述した場合と同じように、コピーを避けることを意図していることが明示的になりますdocument with意図がコピーを無効にしているという行のコメント。

C++ 11はこれを変更せず、コード内でドキュメントを明示的にします。コピーコンストラクターを削除済みとして宣言するのと同じ行に、それが必要であることが文書化されています完全に無効化

最後のコメントとして、C++ 11の機能は、より少ないコードまたはより良いコードでnoncopyableを書くことができるだけでなく、コンパイラーが生成したくないコードを生成しないようにすることです。 。これは、その機能の1つの使用法です。

他の人が持ち出したポイントに加えて...

定義していないプライベートコピーコンストラクタとコピー割り当て演算子があると、誰もコピーを作成できなくなります。ただし、メンバー関数またはフレンド関数がコピーを作成しようとすると、リンク時エラーが発生します。これらの関数を明示的に削除した場所で削除しようとすると、コンパイルエラーが発生します。

私は常にできるだけ早くエラーを発生させます。実行時エラーを後で発生するのではなく、エラーのポイントで発生させる(したがって、変数を読み取るときではなく変数を変更するときにエラーを発生させる)。すべての実行時エラーをリンク時エラーにすると、コードが間違っている可能性がなくなります。すべてのリンク時エラーをコンパイル時エラーにすると、開発がスピードアップし、わずかに役立つエラーメッセージが表示されます。

10
David Stone

読みやすく、コンパイラーがより良いエラーを出すことができます。

「削除された」ことは、特に彼らがクラスをざっと読んでいる場合、読者にとってより明確です。同様に、コンパイラは、一般的な「プライベートメンバーにアクセスしようとしています」エラーを表示する代わりに、コピーできない型をコピーしようとしていることを通知できます。

しかし、実際には、これは単なる便利な機能です。

5

ここの人々は、メンバー関数を定義せずに宣言することを推奨しています。このようなアプローチは移植性がないことを指摘したいと思います。一部のコンパイラ/リンカーでは、メンバー関数を宣言する場合は、使用されていなくても定義する必要があります。 VC++、GCC、clangのみを使用している場合、これを回避できますが、真に移植可能なコードを作成しようとすると、他のコンパイラー(Green Hillsなど)が失敗します。

2
ThreeBit