2 に答える
delete
使用する関数は、 d かどうかをチェックする前に選択されます。コピー コンストラクターが使用可能で、移動コンストラクターがdelete
d の場合でも、移動コンストラクターが 2 つのうちの最良の選択でした。次に、それがdelete
d であることがわかり、エラーが発生します。
同じ例で、移動コンストラクターを d にするのではなく実際に削除した場合、正常delete
にコンパイルされ、コピー コンストラクターを使用するようにフォールバックすることがわかります。
#include <iostream>
#include <memory>
using namespace std;
struct A {
unique_ptr<int> ref;
A(const A&a)
: ref( a.ref.get() ? new int(*a.ref) : nullptr )
{ }
A(const int i) : ref(new int(i)) { }
~A() = default;
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
このクラスには、Move コンストラクターが宣言されていないため (暗黙的であっても)、選択できません。
「フォールバック」はありません。これをオーバーロード解決と呼びます。オーバーロードの解決に複数の候補がある場合は、C++ 標準またはそのドラフトを読むことで見つけることができる複雑な一連の規則に従って、最適な一致が選択されます。
コンストラクターを使用しない例を次に示します。
class X { };
void func(X &&) { cout << "move\n"; } // 1
void func(X const &) { cout << "copy\n"; } // 2
int main()
{
func( X{} );
}
- 現状:「移動」を出力します
- "1" をコメントアウト: "copy" を出力
- "2" をコメントアウト: "move" を出力
- 「1」と「2」をコメントアウト: コンパイルに失敗
オーバーロードの解決では、右辺値を右辺値にバインドすると、左辺値から右辺値へのバインドより優先度が高くなります。
非常によく似た例を次に示します。
void func(int) { cout << "int\n"; } // 1
void func(long) { cout << "long\n"; } // 2
int main()
{
func(1);
}
- そのまま:「int」を出力します
- "1" をコメントアウト: "long" を出力
- "2" をコメントアウト: "int" を出力
- 「1」と「2」をコメントアウト: コンパイルに失敗
オーバーロードの解決では、変換よりも完全一致が優先されます。
このスレッドの 3 つの例には、次のものがあります。
1: 2 つの候補関数。右辺値は右辺値を好む (私の最初の例のように)
A(const A&);
A(A&&); // chosen
2: 2 つの候補関数。右辺値は右辺値を好む (私の最初の例のように)
A(const A&);
A(A&&); // chosen
3: 1 つの候補関数。コンテストなし
A(const A&); // implicitly declared, chosen
前に説明したように、デストラクタがあるため、ケース 3 には A(A&&) の暗黙の宣言はありません。
オーバーロードの解決では、関数本体が存在するかどうかは問題ではなく、関数が (明示的または暗黙的に) 宣言されているかどうかが問題になります。