オーバーロード解決
関数呼び出しをコンパイルするために、コンパイラはまず名前探索を実行する必要があります。これは関数については実引数依存の名前探索を伴うことがあり、関数テンプレートについてはそれに続いてテンプレート実引数推論が行われることがあります。
名前が複数のエンティティを参照する場合、それはオーバーロードされていると言われ、コンパイラはどのオーバーロードを呼び出すかを決定しなければなりません。簡単に言うと、仮引数が実引数に最も近いオーバーロードが呼び出されます。
詳細には、オーバーロード解決は次の手順で進められます。
- 候補関数の集合を構築する。
- その集合を実行可能関数のみに絞り込む。
- その集合を分析して、単一の最良実行可能関数を決定する(これには暗黙の変換シーケンスのランク付けが含まれることがある)。
void f(long); void f(float); f(0L); // calls f(long) f(0); // error: ambiguous overload
関数呼び出しの他に、オーバーロードされた関数名はいくつかの追加の文脈で現れることがあり、そこでは異なるルールが適用されます。詳細はオーバーロードされた関数のアドレスを参照してください。
オーバーロード解決によって関数が選択できない場合、それは使用できません(例:制約に失敗したテンプレート化されたエンティティ)。
目次 |
[編集] 候補関数
オーバーロード解決が始まる前に、名前探索とテンプレート実引数推論によって選択された関数が結合され、候補関数の集合が形成されます。正確な詳細は、オーバーロード解決が行われる文脈に依存します。
[編集] 名前付き関数への呼び出し
関数呼び出し式 E(args) における E が、オーバーロードされた関数および/または関数テンプレートの集合(ただし呼び出し可能オブジェクトは除く)を指名する場合、以下のルールに従います。
- 式 E が PA->B または A.B の形式である場合(ここで A はクラス型 cv
Tを持つ)、B はTのメンバ関数として探索されます。その探索によって見つかった関数宣言が候補関数となります。オーバーロード解決のための実引数リストには、型 cvTの暗黙のオブジェクト実引数が含まれます。 - 式 E が一次式である場合、名前は関数呼び出しの通常ルール(ADL を伴うことがある)に従って探索されます。この探索によって見つかった関数宣言は(探索の仕組み上)以下のいずれかです。
- すべて非メンバ関数(この場合、オーバーロード解決のための実引数リストは関数呼び出し式で使用される実引数リストとまったく同じです)
- すべて何らかのクラス
Tのメンバ関数。この場合、this がスコープ内にあり、それがTまたはTの派生クラスへのポインタであるならば、*this が暗黙のオブジェクト実引数として使用されます。そうでなければ(this がスコープ内にないか、Tを指していない場合)、型Tの偽のオブジェクトが暗黙のオブジェクト実引数として使用され、その後のオーバーロード解決で非静的メンバ関数が選択された場合、プログラムは不正となります。
[編集] クラスオブジェクトへの呼び出し
関数呼び出し式 E(args) における E がクラス型 cv T を持つ場合、以下のようになります。
Tの関数呼び出し演算子は、式 (E).operator() の文脈で名前 operator() を通常の探索によって取得され、見つかったすべての宣言が候補関数の集合に追加されます。TまたはTの基底クラスにある(隠蔽されていない限り)非explicitな各ユーザー定義変換関数について、そのcv修飾子がTのcv修飾子と同じかそれ以上であり、かつ変換関数が以下のいずれかに変換する場合、
- 関数へのポインタ
- 関数へのポインタへの参照
- 関数への参照
- その場合、一意の名前を持つ代理呼び出し関数が候補関数の集合に追加されます。この代理呼び出し関数の最初の仮引数は変換の結果であり、残りの仮引数は変換の結果が受け入れる仮引数リストであり、戻り値の型は変換の結果の戻り値の型です。この代理関数が後続のオーバーロード解決で選択された場合、ユーザー定義変換関数が呼び出され、その後、変換の結果が呼び出されます。
いずれの場合も、オーバーロード解決のための実引数リストは、関数呼び出し式の実引数リストの前に暗黙のオブジェクト実引数 E が付いたものになります(代理関数との照合時には、ユーザー定義変換が自動的に暗黙のオブジェクト実引数を代理関数の最初の実引数に変換します)。
int f1(int); int f2(float); struct A { using fp1 = int(*)(int); operator fp1() { return f1; } // conversion function to pointer to function using fp2 = int(*)(float); operator fp2() { return f2; } // conversion function to pointer to function } a; int i = a(1); // calls f1 via pointer returned from conversion function
[編集] オーバーロードされた演算子への呼び出し
式中の演算子への実引数のうち少なくとも1つがクラス型または列挙型を持つ場合、組込み演算子とユーザー定義演算子オーバーロードの両方がオーバーロード解決に参加し、候補関数の集合は次のように選択されます。
実引数が型 T1 を持つ単項演算子 @(cv修飾子を削除後)、または左オペランドが型 T1、右オペランドが型 T2 を持つ二項演算子 @(cv修飾子を削除後)について、以下の候補関数の集合が準備されます。
operator@ の非修飾名探索(ADL を伴うことがある)によって見つかったすべての宣言。ただし、メンバ関数宣言は無視され、次の外側のスコープへの探索を妨げません。二項演算子の両方のオペランドまたは単項演算子の唯一のオペランドが列挙型を持つ場合、探索集合から非メンバ候補となるのは、その仮引数がその列挙型(またはその列挙型への参照)を持つものだけです。|
4) 書き換えられた候補
すべての場合において、書き換えられた候補は書き換えられた式の文脈では考慮されません。他のすべての演算子については、書き換えられた候補の集合は空です。 |
(C++20以降) |
オーバーロード解決に提出される候補関数の集合は、上記の集合の和集合です。オーバーロード解決のための実引数リストは、演算子のオペランドで構成されます。ただし、operator-> は例外で、2番目のオペランドは関数呼び出しの実引数ではありません(メンバアクセス演算子を参照)。
struct A { operator int(); // user-defined conversion }; A operator+(const A&, const A&); // non-member user-defined operator void m() { A a, b; a + b; // member-candidates: none // non-member candidates: operator+(a, b) // built-in candidates: int(a) + int(b) // overload resolution chooses operator+(a, b) }
オーバーロード解決が組込み候補を選択した場合、クラス型のオペランドからのユーザー定義変換シーケンスは、2番目の標準変換シーケンスを持つことが許可されません。ユーザー定義変換関数は、期待されるオペランド型を直接与えなければなりません。
struct Y { operator int*(); }; // Y is convertible to int* int *a = Y() + 100.0; // error: no operator+ between pointer and double
operator,、単項の operator&、および operator-> について、候補関数の集合に実行可能関数(下記参照)がない場合、演算子は組込みとして再解釈されます。
|
書き換えられた operator<=> 候補が演算子 書き換えられた operator== 候補が演算子 この場合のオーバーロード解決には、書き換えられていない候補を書き換えられた候補よりも優先し、合成されていない書き換えられた候補を合成された書き換えられた候補よりも優先する最終的なタイブレーカーがあります。 この引数の順序を逆にした探索により、operator<=>(std::string, const char*) と operator==(std::string, const char*) を書くだけで、std::string と const char* の間のすべての比較を双方向で生成することが可能になります。詳細はデフォルトの比較を参照してください。 |
(C++20以降) |
[編集] コンストラクタによる初期化
クラス型のオブジェクトが直接初期化またはデフォルト初期化される場合(コピーリスト初期化の文脈でのデフォルト初期化を含む)(C++11以降)、候補関数は初期化されるクラスのすべてのコンストラクタです。実引数リストは初期化子の式リストです。
それ以外の場合、候補関数は初期化されるクラスのすべての変換コンストラクタです。実引数リストは初期化子の式です。
|
コピーリスト初期化の文脈でのデフォルト初期化について、 |
(C++11以降) |
[編集] 変換によるコピー初期化
クラス型のオブジェクトのコピー初期化が、型 cv S の初期化式を初期化されるオブジェクトの型 cv T に変換するためにユーザー定義変換を呼び出す必要がある場合、以下の関数が候補関数となります。
Tのすべての変換コンストラクタSおよびその基底クラスからの(隠蔽されていない限り)TまたはTから派生したクラス、あるいはそれらへの参照への非explicit変換関数。このコピー初期化が cvTの直接初期化シーケンスの一部である場合(cvTへの参照を取るコンストラクタの最初の仮引数に束縛される参照を初期化する場合)、explicit変換関数も考慮されます。
いずれにせよ、オーバーロード解決のための実引数リストは、初期化式である単一の実引数から構成され、これはコンストラクタの最初の実引数または変換関数の暗黙のオブジェクト実引数と比較されます。
[編集] 変換による非クラスの初期化
非クラス型 cv1 T のオブジェクトの初期化が、クラス型 cv S の初期化式から変換するためにユーザー定義変換関数を必要とする場合、以下の関数が候補となります。
Sおよびその基底クラスの(隠蔽されていない限り)非explicitなユーザー定義変換関数で、型Tまたは標準変換シーケンスによってTに変換可能な型、あるいはそのような型への参照を生成するもの。返される型のcv修飾子は、候補関数を選択する目的では無視されます。- これが直接初期化である場合、
Sおよびその基底クラスの(隠蔽されていない限り)explicitなユーザー定義変換関数で、型Tまたは修飾変換によってTに変換可能な型、あるいはそのような型への参照を生成するものも考慮されます。
いずれにせよ、オーバーロード解決のための実引数リストは、初期化式である単一の実引数から構成され、これは変換関数の暗黙のオブジェクト実引数と比較されます。
[編集] 変換による参照の初期化
参照初期化中、cv1 T への参照がクラス型 cv2 S の初期化式からの変換の結果である左辺値または右辺値に束縛される場合、以下の関数が候補集合に選択されます。
Sおよびその基底クラスの(隠蔽されていない限り)非explicitなユーザー定義変換関数で、以下の型へのもの
- (左辺値に変換する場合)cv2
T2への左辺値参照 - (右辺値または関数型の左辺値に変換する場合)cv2
T2または cv2T2への右辺値参照
- (左辺値に変換する場合)cv2
- ここで cv2
T2は cv1Tと参照互換です。
- 直接初期化の場合、
T2がTと同じ型であるか、修飾変換でT型に変換できる場合、explicitなユーザー定義変換関数も考慮されます。
いずれにせよ、オーバーロード解決のための実引数リストは、初期化式である単一の実引数から構成され、これは変換関数の暗黙のオブジェクト実引数と比較されます。
[編集] リスト初期化
非集成体クラス型 T のオブジェクトがリスト初期化される場合、2段階のオーバーロード解決が行われます。
- 第1段階では、候補関数は
Tのすべての初期化子リストコンストラクタであり、オーバーロード解決のための実引数リストは単一の初期化子リスト実引数から構成されます。 - 第1段階でオーバーロード解決が失敗した場合、第2段階に入ります。そこでは、候補関数は
Tのすべてのコンストラクタであり、オーバーロード解決のための実引数リストは初期化子リストの個々の要素から構成されます。
初期化子リストが空で、T がデフォルトコンストラクタを持つ場合、第1段階はスキップされます。
コピーリスト初期化において、第2段階でexplicitコンストラクタが選択された場合、その初期化は不正です(explicitコンストラクタが考慮すらされない他のすべてのコピー初期化とは対照的です)。
[編集] 関数テンプレート候補に関する追加ルール
名前探索で関数テンプレートが見つかった場合、テンプレート実引数推論と明示的なテンプレート実引数のチェックが行われ、この場合に使用できるテンプレート実引数の値(もしあれば)を見つけます。
- 両方が成功した場合、テンプレート実引数を使用して対応する関数テンプレート特殊化の宣言が合成され、それらが候補集合に追加されます。そして、そのような特殊化は、下記のタイブレーカールールで特に指定されていない限り、非テンプレート関数とまったく同じように扱われます。
- 実引数推論が失敗するか、合成された関数テンプレート特殊化が不正になる場合、そのような関数は候補集合に追加されません(SFINAE を参照)。
名前が1つ以上の関数テンプレートと、オーバーロードされた非テンプレート関数の集合の両方を参照する場合、それらの関数とテンプレートから生成された特殊化はすべて候補となります。
詳細は関数テンプレートのオーバーロードを参照してください。
|
コンストラクタテンプレートまたは変換関数テンプレートが、値依存である条件付きexplicit指定子を持つ場合、推論後、文脈が非explicitな候補を要求し、生成された特殊化がexplicitであるならば、それは候補集合から削除されます。 |
(C++20以降) |
[編集] コンストラクタ候補に関する追加ルール
|
deletedとして定義されたデフォルトのムーブコンストラクタおよびムーブ代入演算子は、候補関数の集合から除外されます。 クラス型
|
(C++11以降) |
[編集] メンバ関数候補に関する追加ルール
いずれかの候補関数がメンバ関数(静的または非静的)で、明示的なオブジェクト仮引数を持たないもの(C++23以降)であり、かつコンストラクタでない場合、それは追加の仮引数(暗黙のオブジェクト仮引数)を持つかのように扱われます。これは、それらが呼び出されるオブジェクトを表し、実際の仮引数の最初のものの前に現れます。
同様に、メンバ関数が呼び出されるオブジェクトは、暗黙のオブジェクト実引数として実引数リストの先頭に追加されます。
クラス X のメンバ関数について、暗黙のオブジェクト仮引数の型は、メンバ関数で説明されているように、メンバ関数のcv修飾子と参照修飾子の影響を受けます。
ユーザー定義変換関数は、暗黙のオブジェクト仮引数の型を決定する目的で、暗黙のオブジェクト実引数のメンバであると見なされます。
using宣言によって派生クラスに導入されたメンバ関数は、暗黙のオブジェクト仮引数の型を定義する目的で、派生クラスのメンバであると見なされます。
|
静的メンバ関数については、暗黙のオブジェクト仮引数は任意のオブジェクトに一致すると見なされます。その型は検査されず、変換シーケンスも試みられません。 |
(C++23まで) |
オーバーロード解決の残りについては、暗黙のオブジェクト実引数は他の実引数と区別できませんが、暗黙のオブジェクト仮引数には以下の特別なルールが適用されます。
struct B { void f(int); }; struct A { operator B&(); }; A a; a.B::f(1); // Error: user-defined conversions cannot be applied // to the implicit object parameter static_cast<B&>(a).f(1); // OK
[編集] 実行可能関数
上記のように構築された候補関数の集合が与えられると、オーバーロード解決の次のステップは、実引数と仮引数を調べて、その集合を実行可能関数の集合に減らすことです。
実行可能関数の集合に含まれるためには、候補関数は以下を満たさなければなりません。
M 個ある場合、ちょうど M 個の仮引数を持つ候補関数は実行可能です。M 個より多い仮引数を持ち、M+1 番目の仮引数とそれに続くすべての仮引数がデフォルト実引数を持つ場合、それは実行可能です。オーバーロード解決の残りについては、仮引数リストは M 個で切り捨てられます。|
4) 関数に関連付けられた制約がある場合、それは満たされなければなりません。
|
(C++20以降) |
ユーザー定義変換(変換コンストラクタとユーザー定義変換関数の両方)は、複数のユーザー定義変換を適用可能にするような暗黙の変換シーケンスに参加することは禁止されています。具体的には、変換の対象がコンストラクタの最初の仮引数またはユーザー定義変換関数の暗黙のオブジェクト仮引数であり、かつそのコンストラクタ/ユーザー定義変換が以下の候補である場合、それらは考慮されません。
- ユーザー定義変換によるクラスのコピー初期化,
- 変換関数による非クラス型の初期化,
- 直接参照束縛のための変換関数による初期化,
- クラスのコピー初期化の第2ステップ(直接初期化)中のコンストラクタによる初期化、
struct A { A(int); }; struct B { B(A); }; B b{{0}}; // list-initialization of B // candidates: B(const B&), B(B&&), B(A) // {0} -> B&& not viable: would have to call B(A) // {0} -> const B&: not viable: would have to bind to rvalue, would have to call B(A) // {0} -> A viable. Calls A(int): user-defined conversion to A is not banned |
(C++11以降) |
[編集] 最良実行可能関数
実行可能関数の各ペア F1 と F2 について、i 番目の実引数から i 番目の仮引数への暗黙の変換シーケンスがランク付けされ、どちらがより良いかが決定されます(最初の実引数、静的メンバ関数の暗黙のオブジェクト実引数はランク付けに影響しません)。
F1 が F2 よりも良い関数であると決定されるのは、F1 のすべての実引数に対する暗黙の変換が、F2 のすべての実引数に対する暗黙の変換よりも悪くない場合で、かつ以下のいずれかが成り立つ場合です。
F1 の少なくとも1つの実引数の暗黙の変換が、F2 のその実引数に対応する暗黙の変換よりも良い場合。または、そうでなければ、F1 の結果から初期化される型への標準変換シーケンスが、F2 の結果からの標準変換シーケンスよりも良い場合。または、そうでなければ、|
3) (関数型への参照の直接参照束縛のための変換関数による初期化の文脈でのみ)
F1 の結果が初期化される参照と同じ種類(左辺値または右辺値)の参照であり、F2 の結果がそうでない場合。または、そうでなければ、 |
(C++11以降) |
F1 は非テンプレート関数であり、F2 はテンプレート特殊化である場合。または、そうでなければ、|
6)
F1 と F2 が非テンプレート関数であり、F1 が F2 よりもより部分順序制約されている場合template<typename T = int> struct S { constexpr void f(); // #1 constexpr void f(this S&) requires true; // #2 }; void test() { S<> s; s.f(); // calls #2 } 。または、そうでなければ、
|
(C++20以降) |
|
7)
F1 がクラス D のコンストラクタで、F2 が D の基底クラス B のコンストラクタであり、すべての実引数について F1 と F2 の対応する仮引数が同じ型を持つ場合struct A { A(int = 0); }; struct B: A { using A::A; B(); }; B b; // OK, B::B() 。または、そうでなければ、
|
(C++11以降) |
|
8)
F2 が書き換えられた候補であり、F1 がそうでない場合。または、そうでなければ、9)
F1 と F2 が両方とも書き換えられた候補であり、F2 がパラメータの順序が逆になった合成された書き換え候補であり、F1 がそうでない場合。または、そうでなければ、 |
(C++20以降) |
|
12)
F1 が非テンプレートのコンストラクタから生成され、F2 がコンストラクタテンプレートから生成された場合template<class T> struct A { using value_type = T; A(value_type); // #1 A(const A&); // #2 A(T, T, int); // #3 template<class U> A(int, T, U); // #4 }; // #5 is A(A), the copy deduction candidate A x(1, 2, 3); // uses #3, generated from a non-template constructor template<class T> A(T) -> A<T>; // #6, less specialized than #5 A a (42); // uses #6 to deduce A<int> and #1 to initialize A b = a; // uses #5 to deduce A<int> and #2 to initialize template<class T> A(A<T>) -> A<A<T>>; // #7, as specialized as #5 A b2 = a; // uses #7 to deduce A<A<int>> and #1 to initialize |
(C++17以降) |
これらのペアごとの比較は、すべての実行可能関数に適用されます。もし、ちょうど1つの実行可能関数が他のすべての関数よりも優れている場合、オーバーロード解決は成功し、この関数が呼び出されます。そうでなければ、コンパイルは失敗します。
void Fcn(const int*, short); // overload #1 void Fcn(int*, int); // overload #2 int i; short s = 0; void f() { Fcn(&i, 1L); // 1st argument: &i -> int* is better than &i -> const int* // 2nd argument: 1L -> short and 1L -> int are equivalent // calls Fcn(int*, int) Fcn(&i, 'c'); // 1st argument: &i -> int* is better than &i -> const int* // 2nd argument: 'c' -> int is better than 'c' -> short // calls Fcn(int*, int) Fcn(&i, s); // 1st argument: &i -> int* is better than &i -> const int* // 2nd argument: s -> short is better than s -> int // no winner, compilation error }
最良実行可能関数が複数の宣言が見つかった関数に解決され、かつ、これらの宣言のうちのいずれか2つが異なるスコープに存在し、その関数を実行可能にしたデフォルト引数を指定している場合、プログラムは不正です。
namespace A { extern "C" void f(int = 5); } namespace B { extern "C" void f(int = 5); } using A::f; using B::f; void use() { f(3); // OK, default argument was not used for viability f(); // error: found default argument twice }
[編集] 暗黙の変換シーケンスのランク付け
オーバーロード解決によって考慮される実引数-仮引数間の暗黙の変換シーケンスは、コピー初期化(非参照仮引数に対して)で使用される暗黙の変換に対応します。ただし、暗黙のオブジェクト仮引数への変換や代入演算子の左辺への変換を考慮する場合、一時オブジェクトを作成する変換は考慮されません。仮引数が静的メンバ関数の暗黙のオブジェクト仮引数である場合、暗黙の変換シーケンスは、他のどの標準変換シーケンスよりも良くも悪くもない標準変換シーケンスです。(C++23以降)
各種類の標準変換シーケンスには、3つのランクのいずれかが割り当てられます。
標準変換シーケンスのランクは、それが保持する標準変換の中で最も悪いランクになります(最大3つの変換があるかもしれません)。
参照仮引数を実引数式に直接束縛するのは、恒等変換または派生から基底への変換のいずれかです。
struct Base {}; struct Derived : Base {} d; int f(Base&); // overload #1 int f(Derived&); // overload #2 int i = f(d); // d -> Derived& has rank Exact Match // d -> Base& has rank Conversion // calls f(Derived&)
変換シーケンスのランク付けは型と値カテゴリのみで動作するため、ビットフィールドはランク付けの目的で参照実引数に束縛できますが、その関数が選択されると不正になります。
S1 は、標準変換シーケンス S2 よりも良いです、もしS1 が S2 の真の部分シーケンスである場合(左辺値変換を除く)。恒等変換シーケンスは、非恒等変換の部分シーケンスと見なされます。または、そうでなければ、S1 のランクが S2 のランクよりも良い場合。または、そうでなければ、S1 と S2 の両方が、参照修飾されたメンバ関数の暗黙のオブジェクト仮引数以外のものへの参照仮引数に束縛しており、S1 は右辺値参照を右辺値に束縛し、S2 は左辺値参照を右辺値に束縛する場合int i; int f1(); int g(const int&); // overload #1 int g(const int&&); // overload #2 int j = g(i); // lvalue int -> const int& is the only valid conversion int k = g(f1()); // rvalue int -> const int&& better than rvalue int -> const int&
S1 と S2 の両方が参照仮引数に束縛しており、S1 は関数への左辺値参照を束縛し、S2 は関数への右辺値参照を束縛する場合int f(void(&)()); // overload #1 int f(void(&&)()); // overload #2 void g(); int i1 = f(g); // calls #1
S1 と S2 が修飾変換においてのみ異なる場合、かつ|
|
(C++20まで) |
|
|
(C++20以降) |
int f(const int*); int f(int*); int i; int j = f(&i); // &i -> int* is better than &i -> const int*, calls f(int*)
S1 と S2 の両方が、トップレベルのcv修飾のみが異なる参照仮引数に束縛しており、S1 の型が S2 の型よりもcv修飾が少ない場合int f(const int &); // overload #1 int f(int &); // overload #2 (both references) int g(const int &); // overload #1 int g(int); // overload #2 int i; int j = f(i); // lvalue i -> int& is better than lvalue int -> const int& // calls f(int&) int k = g(i); // lvalue i -> const int& ranks Exact Match // lvalue i -> rvalue int ranks Exact Match // ambiguous overload: compilation error
S1 と S2 が同じ参照型「Tへの参照」を束縛し、それぞれソース型 V1 と V2 を持ち、V1* から T* への標準変換シーケンスが V2* から T* への標準変換シーケンスよりも良い場合struct Z {}; struct A { operator Z&(); operator const Z&(); // overload #1 }; struct B { operator Z(); operator const Z&&(); // overload #2 }; const Z& r1 = A(); // OK, uses #1 const Z&& r2 = B(); // OK, uses #2
U1 は、ユーザー定義変換シーケンス U2 よりも良いです。もし、それらが同じコンストラクタ/ユーザー定義変換関数を呼び出すか、または集成体初期化で同じクラスを初期化し、どちらの場合も U1 の2番目の標準変換シーケンスが U2 の2番目の標準変換シーケンスよりも良い場合struct A { operator short(); // user-defined conversion function } a; int f(int); // overload #1 int f(float); // overload #2 int i = f(a); // A -> short, followed by short -> int (rank Promotion) // A -> short, followed by short -> float (rank Conversion) // calls f(int)
L1 は、リスト初期化シーケンス L2 よりも良いです。もし、L1 が std::initializer_list 仮引数を初期化し、L2 がそうでない場合。void f1(int); // #1 void f1(std::initializer_list<long>); // #2 void g1() { f1({42}); } // chooses #2 void f2(std::pair<const char*, const char*>); // #3 void f2(std::initializer_list<std::string>); // #4 void g2() { f2({"foo", "bar"}); } // chooses #4
|
6) リスト初期化シーケンス L1 は、リスト初期化シーケンス L2 よりも良いです。もし、対応する仮引数が配列への参照であり、L1が型 "N1 T の配列" に変換し、L2が型 "N2 T の配列" に変換し、N1がN2よりも小さい場合。 |
(C++11以降) (C++20まで) |
|
6) リスト初期化シーケンス L1 は、リスト初期化シーケンス L2 よりも良いです。もし、対応する仮引数が配列への参照であり、L1とL2が同じ要素型の配列に変換し、かつ以下のいずれかの場合。
void f(int (&&)[] ); // overload #1 void f(double (&&)[] ); // overload #2 void f(int (&&)[2]); // overload #3 f({1}); // #1: Better than #2 due to conversion, better than #3 due to bounds f({1.0}); // #2: double -> double is better than double -> int f({1.0, 2.0}); // #2: double -> double is better than double -> int f({1, 2}); // #3: -> int[2] is better than -> int[], // and int -> int is better than int -> double |
(C++20以降) |
2つの変換シーケンスが同じランクを持つために区別できない場合、以下の追加ルールが適用されます。
| (C++11以降) |
|
3) 浮動小数点型
FP1 と浮動小数点型 FP2 の間のいずれかの方向の変換は、FP1 と算術型 T3 の間の同じ方向の変換よりも良いです、もし
int f(std::float32_t); int f(std::float64_t); int f(long long); float x; std::float16_t y; int i = f(x); // calls f(std::float32_t) on implementations where // float and std::float32_t have equal conversion ranks int j = f(y); // error: ambiguous, no equal conversion rank |
(C++23から) |
Mid が Base から(直接または間接的に)派生し、Derived が Mid から(直接または間接的に)派生する場合Derived* から Mid* への変換は Derived* から Base* への変換より良いDerived から Mid& または Mid&& への変換は Derived から Base& または Base&& への変換より良いBase::* から Mid::* への変換は Base::* から Derived::* への変換より良いDerived から Mid への変換は Derived から Base への変換より良いMid* から Base* への変換は Derived* から Base* への変換より良いMid から Base& または Base&& への変換は Derived から Base& または Base&& への変換より良いMid::* から Derived::* への変換は Base::* から Derived::* への変換より良いMid から Base への変換は Derived から Base への変換より良い曖昧な変換シーケンスは、ユーザー定義変換シーケンスとしてランク付けされます。なぜなら、1つの実引数に対して複数の変換シーケンスが存在できるのは、それらが異なるユーザー定義変換を伴う場合のみだからです。
class B; class A { A (B&);}; // converting constructor class B { operator A (); }; // user-defined conversion function class C { C (B&); }; // converting constructor void f(A) {} // overload #1 void f(C) {} // overload #2 B b; f(b); // B -> A via ctor or B -> A via function (ambiguous conversion) // b -> C via ctor (user-defined conversion) // the conversions for overload #1 and for overload #2 // are indistinguishable; compilation fails
[編集] リスト初期化における暗黙の変換シーケンス
リスト初期化では、実引数は波括弧初期化リストであり、これは式ではないため、オーバーロード解決のための仮引数型への暗黙の変換シーケンスは、以下の特別なルールによって決定されます。
- 仮引数型が何らかの集成体
Xで、初期化子リストが同じまたは派生クラスの(cv修飾されている可能性のある)単一の要素からなる場合、暗黙の変換シーケンスはその要素を仮引数型に変換するために必要なものになります。 - そうでなく、仮引数型が文字配列への参照で、初期化子リストが適切な型の文字列リテラルである単一の要素を持つ場合、暗黙の変換シーケンスは恒等変換です。
- そうでなく、仮引数型が std::initializer_list<X> であり、初期化子リストのすべての要素から
Xへの縮小変換でない暗黙の変換が存在する場合、オーバーロード解決のための暗黙の変換シーケンスは、必要な最も悪い変換です。波括弧初期化リストが空の場合、変換シーケンスは恒等変換です。
struct A { A(std::initializer_list<double>); // #1 A(std::initializer_list<complex<double>>); // #2 A(std::initializer_list<std::string>); // #3 }; A a{1.0, 2.0}; // selects #1 (rvalue double -> double: identity conv) void g(A); g({"foo", "bar"}); // selects #3 (lvalue const char[4] -> std::string: user-def conv)
- そうでなく、仮引数型が「N T の配列」である場合(これは配列への参照でのみ発生します)、初期化子リストは N 個以下の要素を持たなければならず、リストのすべての要素(またはリストが N より短い場合は空の波括弧ペア
{})をTに変換するために必要な最も悪い暗黙の変換が使用されます。
|
(C++20以降) |
typedef int IA[3]; void h(const IA&); void g(int (&&)[]); h({1, 2, 3}); // int->int identity conversion g({1, 2, 3}); // same as above since C++20
- そうでなく、仮引数型が非集成体クラス型
Xである場合、オーバーロード解決は実引数初期化子リストから初期化するために X のコンストラクタ C を選択します。
- C が初期化子リストコンストラクタではなく、初期化子リストが単一の(cv修飾されている可能性のある)X の要素を持つ場合、暗黙の変換シーケンスは完全一致ランクを持ちます。初期化子リストが単一の(cv修飾されている可能性のある)X から派生した型の要素を持つ場合、暗黙の変換シーケンスは変換ランクを持ちます。(集成体との違いに注意:集成体は集成体初期化を考慮する前に単一要素の初期化リストから直接初期化しますが、非集成体は他のどのコンストラクタよりも先に initializer_list コンストラクタを考慮します)
- そうでなければ、暗黙の変換シーケンスは、2番目の標準変換シーケンスが恒等変換であるユーザー定義変換シーケンスです。
複数のコンストラクタが実行可能であるが、他のものより良いものがない場合、暗黙の変換シーケンスは曖昧な変換シーケンスです。
struct A { A(std::initializer_list<int>); }; void f(A); struct B { B(int, double); }; void g(B); g({'a', 'b'}); // calls g(B(int, double)), user-defined conversion // g({1.0, 1,0}); // error: double->int is narrowing, not allowed in list-init void f(B); // f({'a', 'b'}); // f(A) and f(B) both user-defined conversions
- そうでなく、仮引数型が集成体初期化によって初期化子リストから初期化できる集成体である場合、暗黙の変換シーケンスは、2番目の標準変換シーケンスが恒等変換であるユーザー定義変換シーケンスです。
struct A { int m1; double m2; }; void f(A); f({'a', 'b'}); // calls f(A(int, double)), user-defined conversion
- そうでなく、仮引数が参照である場合、参照初期化ルールが適用されます。
struct A { int m1; double m2; }; void f(const A&); f({'a', 'b'}); // temporary created, f(A(int, double)) called. User-defined conversion
- そうでなく、仮引数型がクラスでなく、初期化子リストが1つの要素を持つ場合、暗黙の変換シーケンスはその要素を仮引数型に変換するために必要なものです。
- そうでなく、仮引数型がクラス型でなく、初期化子リストが要素を持たない場合、暗黙の変換シーケンスは恒等変換です。
|
実引数が指示付き初期化子リストであり、仮引数が参照でない場合、仮引数がその初期化子リストから集成体初期化のルールに従って初期化できる集成体型を持つ場合にのみ変換が可能です。その場合、暗黙の変換シーケンスは、2番目の標準変換シーケンスが恒等変換であるユーザー定義変換シーケンスです。 オーバーロード解決後、選択されたオーバーロードに対して集成体のメンバの宣言順序が一致しない場合、仮引数の初期化は不正となります。 struct A { int x, y; }; struct B { int y, x; }; void f(A a, int); // #1 void f(B b, ...); // #2 void g(A a); // #3 void g(B b); // #4 void h() { f({.x = 1, .y = 2}, 0); // OK; calls #1 f({.y = 2, .x = 1}, 0); // error: selects #1, initialization of a fails // due to non-matching member order g({.x = 1, .y = 2}); // error: ambiguous between #3 and #4 }
|
(C++20以降) |
[編集] 不具合報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 1 | C++98 | 同じ関数が(異なるスコープからの)異なる デフォルト引数を持つ可能性がある場合に 選択されたときの振る舞いが未規定だった |
この場合、プログラムは 不正となる |
| CWG 83 | C++98 | 文字列リテラルから char* への変換シーケンスが const char* への変換よりも良かった 前者が非推奨であるにもかかわらず |
非推奨の変換のランクが 下げられた(C++11で 削除された) |
| CWG 162 | C++98 | &F(args) の場合に F によって名指されたオーバーロード集合に非静的メンバ関数が含まれていると不正だった |
オーバーロード解決が非静的 メンバ関数を選択した場合にのみ 不正となる |
| CWG 233 | C++98 | 参照とポインタがユーザー定義変換を伴う オーバーロード解決で一貫性なく扱われていた |
一貫して扱われる ようになった |
| CWG 280 | C++98 | アクセス不能な基底クラスで宣言された 変換関数に対して代理呼び出し関数が 候補関数の集合に追加されていなかった |
アクセシビリティの制約を 削除。代理呼び出し関数が 選択され、対応する変換 関数が呼び出せない場合に プログラムは不正と なる |
| CWG 415 | C++98 | 関数テンプレートが候補として選択されたとき、 その特殊化はテンプレート実引数推論を 使用してインスタンス化されていた |
この場合、インスタンス化は 行われず、その宣言が 合成される |
| CWG 495 | C++98 | 実引数の暗黙の変換が同等に良い場合、 非テンプレート変換関数は常に変換関数 テンプレートよりも良かった。後者がより良い 標準変換シーケンスを持つ可能性があっても |
標準変換シーケンスは 特殊化レベルの前に 比較される |
| CWG 1307 | C++11 | 配列のサイズに基づくオーバーロード解決が規定されていなかった | 可能な場合、より 短い配列がより良い |
| CWG 1328 | C++11 | 変換結果への参照を束縛する際の 候補関数の決定が明確でなかった |
明確化された |
| 明確化された | C++98 | 修飾変換は、標準変換シーケンスを比較する際に 参照束縛の前にチェックされていた |
順序が逆転した |
| CWG 1385 | C++11 | 参照修飾子付きで宣言された非explicitな ユーザー定義変換関数に対応する代理関数がなかった |
対応する代理関数を 持つようになった |
| CWG 1467 | C++11 | 集成体と配列の同型リスト初期化が 省略されていた |
初期化が定義された |
| CWG 1601 | C++11 | enumからその基底型への変換が、 固定された基底型を優先しなかった |
固定型が、それが昇格 する型よりも優先される |
| CWG 1608 | C++98 | 実引数が型 T1 を持つ単項演算子 @ のメンバ候補の集合が、 T1 が現在定義中のクラスの場合に空だった |
この場合、集合はT1::operator@ の修飾名探索の結果となる |
| CWG 1687 | C++98 | 組込み候補がオーバーロード解決で選択されたとき、 オペランドは制限なく変換されていた |
クラス型のオペランドのみを変換し、 2番目の標準変換シーケンスを 無効にした |
| CWG 2052 | C++98 | 不正な形式の合成された関数テンプレート特殊化が 候補集合に追加され、プログラムを不正にすることがあった |
それらは候補集合に 追加されない |
| CWG 2076 | C++11 | リスト初期化中にネストした初期化子リスト内の 単一の初期化子にユーザー定義変換が適用されていた CWG issue 1467 の解決により |
適用されない |
| CWG 2137 | C++11 | X を {X} でリスト初期化する際に、初期化子リストコンストラクタがコピコンストラクタに負けていた |
非集成体は初期化子 リストを先に考慮する |
| CWG 2273 | C++11 | 継承されたコンストラクタと 非継承のコンストラクタの間にタイブレーカーがなかった |
非継承のコンストラクタが勝つ |
| CWG 2673 | C++20 | 書き換えられた非メンバ候補と同じ 仮引数リストを持つ組込み候補が 組込み候補のリストに追加されていた |
追加されない |
| CWG 2712 | C++98 | 組込み代入演算子が考慮されるとき、 最初の仮引数は一時オブジェクトに束縛 できなかったが、これは既に不可能だった[1] |
冗長な要件が 削除された |
| CWG 2713 | C++20 | 指示付き初期化子リストに関する変換の 制限が、仮引数が参照であっても適用されていた |
この場合は制限されない |
| CWG 2789 | C++23 | 明示的なオブジェクト仮引数が 仮引数型リストの比較に含まれていた |
除外された |
| CWG 2856 | C++11 | コピーリスト初期化の文脈でのデフォルト 初期化のオーバーロード解決は変換コンストラクタのみを考慮していた |
すべてのコンストラクタを考慮する |
| CWG 2919 | C++98 | 変換による参照初期化の候補集合は、 初期化のターゲット型に依存していた |
変換のターゲット型に 依存する |
| P2468R2 | C++20 | operator== に基づく書き換え候補は、 一致する operator!= が存在しても a != b のために追加される |
追加されない |
- ↑ 組込み代入演算子の最初の仮引数の型は「おそらくvolatile修飾された
Tへの左辺値参照」です。この型の参照は一時オブジェクトに束縛できません。
[編集] 参照
- C++23標準 (ISO/IEC 14882:2024)
- 12.2 オーバーロード解決 [over.match]
- C++20 standard (ISO/IEC 14882:2020)
- 12.4 オーバーロード解決 [over.match]
- C++17 standard (ISO/IEC 14882:2017)
- 16.3 オーバーロード解決 [over.match]
- C++14 standard (ISO/IEC 14882:2014)
- 13.3 オーバーロード解決 [over.match]
- C++11 standard (ISO/IEC 14882:2011)
- 13.3 オーバーロード解決 [over.match]
- C++03 標準 (ISO/IEC 14882:2003)
- 13.3 オーバーロード解決 [over.match]