Foo f = Foo();// 'Foo::Foo(Foo)' の呼び出しに一致する関数がありません … あれ?
-
03-10-2019 - |
質問
class Foo
{
public:
explicit Foo() {}
explicit Foo(Foo&) {}
};
Foo d = Foo();
エラー:「Foo::Foo(Foo)」の呼び出しに一致する関数がありません
変えてみた Foo(Foo&)
に Foo(Foo)
エラーが示すように、私の知る限り、これは有効なコンストラクターではありません。そして、確かに次の結果が得られます。
エラー:無効なコンストラクター。おそらく「Foo (const Foo&)」という意味でしょう
何が与えますか?これを解決するにはどうすればよいですか?(ちなみにこれはGCCにあります)
解決
コピー コンストラクターには疑わしいものが 2 つあります。
まず、コピーコンストラクターを明示的にしたので (これは疑わしいことですが)、(理論的には) 次のことを行う必要があります。
Foo d( (Foo()) );
次に、コピー コンストラクターは、 const
参照。つまり、一時的な参照では使用できません。 Foo
.
個人的には削除したいです explicit
コピーコンストラクターから取得して、 const
できれば参考に。
注意してください。 explicit
デフォルトのコンストラクターでは効果がありません。[*] explicit
単一のパラメータで呼び出すことができるコンストラクタにのみ影響します。これにより、それらが暗黙的な変換に使用されるのを防ぎます。ゼロまたは 2 つ以上のパラメーターのみを取るコンストラクターの場合、効果はありません。
[注記:次のような違いがある可能性があります。
Foo d;
そして
Foo d = Foo();
ただし、この場合はユーザーが宣言したデフォルトのコンストラクターがあるため、これは適用されません。]
編集:[*] これを再確認したところ、12.3.1 [class.conv.ctor] にはデフォルトのコンストラクターを作成できると記載されています explicit
. 。この場合、コンストラクターは実行に使用されます。 デフォルトの初期化 または 値の初期化. 。正直に言うと、ユーザーが宣言したコンストラクターがある場合、それは非POD型であり、非POD型のローカルオブジェクトであっても、初期化子がなければデフォルトで初期化されるため、この値の意味がわかりません。この条項で述べていることは、 explicit
デフォルトのコンストラクター。おそらく誰かがそれが違いを生む特殊なケースを指摘してくれるかもしれないが、今のところ私にはどのような効果があるのか分からない explicit
デフォルトのコンストラクターにあります。
他のヒント
これらのコンストラクターのいずれかを明示的であるとマークしたくありません。コンパイラは、それらの両方、特にコピーコンストラクターを暗黙的に使用する必要があります。それらを明示的にマークすることで何を達成しようとしていますか?
まず、デフォルトのコンストラクターもコピーコンストラクターも explicit
. 。コンストラクターを作るだけです explicit
そのタイプからの暗黙の変換を防ぐために、他のタイプの単一の引数が必要な場合。コピーコンストラクターはクラス自体への参照を取得するため、望ましくない変換の危険はありません。
第二に、コピーコンストラクターが const
参照。
第3、 Foo f;
クラスFOOのデフォルトで構成されたオブジェクトを持つ正しい方法です。ご了承ください Foo f();
コンパイラがそれを機能の宣言として解釈するので、間違っています f()
クラスのオブジェクトを返します Foo
.
第四に、独自のコピーコンストラクターを書いた場合は、割り当てオペレーターも書く必要があります。
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またはAnblebensの投稿を参照してください。
コピーコンストラクターはそうすべきではありません 明示的 (これは、ここでも、価値を通過したり返還したりするときなど、他の多くの完全に合理的なコンテキストでは不可能になります)。
次に、議論を取る必要があります const 参照してください。それ以外の場合は、タイムリーにバインドできないためです。
Foo f = Foo();
^^^^^
|
--- this is a temporary that cannot be passed to a function
that accepts a non-const reference
さらに、デフォルトのコンストラクターを作成する理由はありません 明示的: :このキーワードは、正確に1つの引数で呼び出すことができるコンストラクター(コピーコンストラクター以外)に対してのみ理にかなっています。その場合、そのコンストラクターを介して他のタイプのfooへの暗黙的な変換を防ぎます。たとえば、コンストラクターがandを取る場合 int そうだった 明示的, 、このような状況はコンパイルされません。
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が提案している)は、コピーCTORを使用して排除することです。もう1つは、適切なコピーCTORを書くことです。
Foo (Foo const &) {}
あなたは一般的に両方をしたいです。
編集:それを見ると、私の最後のコメントは簡単に構築できます。かなりの数のクラスがします いいえ コピーctorが必要ですが もしも コピーCTORが必要です。通常、上記のフォームを使用する必要があります(明示的ではなく、パラメーターとしてconst Referenceを使用します)。
class Foo
{
public:
explicit Foo() {}
explicit Foo(const Foo&) {}
};
Foo d = Foo()