これを読み続ける前に、コピー初期化と直接初期化の間に C++ に違いはありますか?を読んでください。まず、それが何について話しているのかを理解していることを確認してください。
最初にここでルールを要約します (標準 n3225 8.5/16、13.3.1.3、13.3.1.4、および 13.3.1.5 を読んでください)。
1) 直接初期化の場合、すべてのコンストラクターはオーバーロード セットと見なされ、オーバーロード解決はオーバーロード解決ルールに従って最適なものを選択します。
2) コピーの初期化の場合、コピー元の型がコピー先の型と同じか、コピー先の型から派生したものである場合、ルールは上記と同じですが、変換するコンストラクター (明示的なコンストラクターがないコンストラクター) のみがオーバーロード セットと見なされる点が異なります。これは実際には、明示的なコピー/移動コンストラクターがオーバーロード セットと見なされないことを意味します。
3) 上記 (2) に含まれないコピーの初期化のケース (ソースの型が宛先の型とは異なり、宛先の型から派生していない) については、まず、ソースの型から宛先の型に変換できるユーザー定義の変換シーケンスを検討するか、または (変換関数を使用する場合) をその派生クラスに変換します。変換が成功した場合、結果は宛先オブジェクトを直接初期化するために使用されます。
3.1) このユーザー定義の変換シーケンスでは、8.5/16 および 13.3.1.4 の規則に従って、変換する ctor (非明示的な ctor) と非明示的な変換関数の両方が考慮されます。
3.2) 結果の prvalue は、(1) に記載されている規則に従って、宛先オブジェクトを直接初期化します。8.5 /16 を参照してください。
さて、ルールについては十分です。いくつかの奇妙なコードを見てみましょう。私の推論がどこで間違っているのか、または単にすべてのコンパイラが間違っているのか、まったくわかりません。助けてください、ありがとう。
struct A
{
A (int) { }
A() { }
explicit A(const A&) { }
};
struct B
{
operator A() { return 2; }
//1) visual c++ and clang passes this
//gcc 4.4.3 denies this, says no viable constructor available
};
int main()
{
B b;
A a = b;
//2) oops, all compilers deny this
}
私の理解では、(1)については、
operator A() { return 2; }
C++ には関数 return をコピー初期化と見なす規則があるため、上記の規則に従って、2 は最初に暗黙的に A に変換されますが、A にはコンストラクタ A(int) があるため、これは問題ないはずです。次に、変換された一時的な prvalue を使用して、返されたオブジェクトを直接初期化します。直接初期化では明示的なコピー コンストラクターを使用できるため、これも問題ありません。したがって、GCCは間違っています。
(2)については、
A a = b;
私の理解では、最初に b は演算子 A() によって暗黙的に A に変換され、次に変換された値を使用して a を直接初期化します。これはもちろん明示的なコピー コンストラクターを呼び出すことができますか? したがって、これはコンパイルに合格するはずで、すべてのコンパイラが間違っていますか?
(2) の場合、Visual C++ と clang の両方に「エラー、B から A に変換できません」のようなエラーがありますが、A のコピー コンストラクターで明示的なキーワードを削除すると、エラーはなくなります。
読んでくれてありがとう。
編集 1
誰かがまだ私の意図を理解していないので、8.5/16 から次の標準を引用します。
それ以外の場合 (つまり、残りのコピー初期化の場合)、ソース型から宛先型、または (変換関数が使用されている場合) その派生クラスに変換できるユーザー定義の変換シーケンスは、13.3 で説明されているように列挙されます。 1.4 であり、過負荷解決 (13.3) によって最適なものが選択されます。変換ができないか、あいまいな場合は、初期化の形式が正しくありません。選択された関数は、初期化式を引数として呼び出されます。関数がコンストラクターの場合、呼び出しは、目的の型の cv 修飾されていないバージョンの一時を初期化します。一時的なものは prvalue です。呼び出しの結果 (コンストラクターの場合は一時的なもの) は、上記の規則に従って、直接初期化するために使用されます。コピー初期化の宛先であるオブジェクト。場合によっては、中間結果を初期化されるオブジェクトに直接構築することにより、この直接初期化に固有のコピーを排除する実装が許可されます。12.2、12.8を参照。
ユーザー定義の変換後に直接初期化することについて言及していることに注意してください。つまり、私の理解では、次のコードは、私がコメントしたようにルールに従う必要があります。これは、clang、coomeau online、visual c++ の両方で確認されていますが、GCC 4.4.3 は (1) と (2) の両方で失敗します。これは奇妙なルールですが、標準からの推論に従います。
struct A
{
A (int) { }
A() { }
explicit A(const A&) { }
};
int main()
{
A a = 2; //1)OK, first convert, then direct-initialize
A a = (A)2; //2)oops, constructor explicit, not viable here!
}