10 に答える
コピー コンストラクターには、疑わしい点が 2 つあります。
まず、コピー コンストラクターを明示的に作成したため (これは疑わしいことです)、(理論的には) 次のようにする必要があります。
Foo d( (Foo()) );
次に、コピー コンストラクターは参照ではなく参照を取りますconst
。つまり、一時的なFoo
.
個人的には、コピーコンストラクターから削除して、可能であれば参照explicit
を取るようにします。const
デフォルトのコンストラクターには効果がないことに注意してexplicit
ください。[*]explicit
は、単一のパラメーターで呼び出すことができるコンストラクターにのみ効果があります。それらが暗黙的な変換に使用されるのを防ぎます。ゼロまたは 2 つ以上のパラメーターのみを受け取るコンストラクターの場合、効果はありません。
[注: 以下の間に違いがある可能性があります。
Foo d;
と
Foo d = Foo();
ただし、この場合、ユーザーが宣言したデフォルトのコンストラクターがあるため、これは適用されません。]
編集:
[*]これを再確認したところ、12.3.1 [class.conv.ctor]はデフォルトのコンストラクターを作成できると言っていますexplicit
。この場合、コンストラクターはdefault-initializationまたはvalue-initializationを実行するために使用されます。正直なところ、ユーザーが宣言したコンストラクターがある場合、それは非 POD 型であり、非 POD 型のローカル オブジェクトでさえ、初期化子がない場合はデフォルトで初期化されるため、これの値を理解していません。この句は、explicit
デフォルトのコンストラクターで実行できると述べています。おそらく、誰かが違いを生むコーナーケースを指摘できるかもしれませんが、今のところexplicit
、デフォルトのコンストラクターにどのような影響があるかわかりません。
これらのコンストラクターのいずれかを明示的にマークする必要はありません。コンパイラーは、それらの両方、特にコピー コンストラクターを暗黙的に使用する必要があります。それらを明示的にマークすることで何を達成しようとしていますか?
まず、デフォルト コンストラクターもコピー コンストラクターも であってはなりませんexplicit
。explicit
その型からの暗黙的な変換を防ぐために、他の型の単一の引数を取る場合にのみコンストラクターを作成する必要があります。コピー コンストラクターはクラス自体への参照を取るため、不要な変換の危険はありません。
次に、コピー コンストラクターがconst
参照を受け取るようにします。
3つ目Foo f;
は、デフォルトで構築されたクラス foo のオブジェクトを持つ正しい方法です。コンパイラはそれを class のオブジェクトを返すFoo f();
関数の宣言として解釈するため、これは間違っていることに注意してください。f()
Foo
第 4 に、独自のコピー コンストラクターを作成した場合は、代入演算子も作成する必要があります。
class Foo
{
Foo() {} // no need to make explicit. Nothing to convert from.
Foo(const &Foo f) {} // again, nothing wrong with conversion from Foo to Foo
explicit Foo(int a) {} // need explicit to prevent accidental passing of an int
// to a function that takes Foo as an argument
};
明示なしで試してみませんか?私はそれを考えます:
Foo foo = Foo()
暗黙的なコピーを作成するため、明示的なコピー コンストラクターはトリガーされません。
編集:
これは答えの半分にすぎません。const が必要な理由については、Charles Bailey または UncleBens の投稿を参照してください。
コピー コンストラクターは明示的であってはなりません(これにより、ここや、値渡しや値渡しなど、他の多くの完全に合理的なコンテキストで呼び出すことができなくなります)。
次に、 const参照によって引数を取得する必要があります。そうしないと、一時変数にバインドできないためです。
Foo f = Foo();
^^^^^
|
--- this is a temporary that cannot be passed to a function
that accepts a non-const reference
さらに、デフォルトのコンストラクターを明示的にする理由はありません。このキーワードは、正確に 1 つの引数で呼び出すことができるコンストラクター (コピー コンストラクター以外) に対してのみ意味があります。コンストラクタ。たとえば、intを取るコンストラクターがexplicitである場合、次のような状況はコンパイルされません。
Foo f;
f = 1; //assuming no operator= overload for (types convertible from) int
//this implicitly performs f = Foo(1);
Foo g = 10;
void x(Foo);
x(20);
概して:
class Foo
{
public:
Foo();
Foo(const Foo&);
//...
};
Foo x = Foo();
さらに、これらのコンストラクターのどちらも何もしない場合は、それらを定義する必要はまったくありません。コンパイラーはそれらを自動的に提供します (ただし、他のコンストラクターを定義すると、デフォルトのコンストラクターは自動的に生成されません)。
Foo d = Foo();
する必要があります
Foo d;
最初の行は Foo インスタンスを作成し、それを d にコピーします。
あなたの問題はインスタンス化にあります。Foo d = Foo();
デフォルトのコンストラクターは必要ありません。
クラスを同じに保ちますが、インスタンス化のためにこれを試してください:
Foo d;
Foo d = Foo(arguments);
実際、パラメーターを使用して構築する必要さえありません。それは次のようになります。
Foo d(arguments);
コンパイラはあなたに言っています...これを使用してください:
Foo(const Foo&) {}
この問題は、2 つの方法のいずれかで解決できます。1 つ (Randolpho によって既に提案されています) は、copy ctor の使用を排除することです。もう 1 つは、適切なコピー ctor を作成することです。
Foo (Foo const &) {}
通常、両方を実行する必要があります。
編集:それを見ると、私の最後のコメントは誤解されやすい. かなりの数のクラスはコピー ctor をまったく必要としませんが、コピー ctor が必要な場合は、通常は上記の形式にする必要があります (明示的ではなく、パラメーターとして const 参照を使用します)。
class Foo
{
public:
explicit Foo() {}
explicit Foo(const Foo&) {}
};
Foo d = Foo()