オーバーロード解決
関数呼び出しをコンパイルするために、コンパイラはまず、名前探索を行わなければなりません (これは、関数の場合は実引数依存の名前探索を発生させることがあり、また、関数テンプレートの場合はテンプレートの実引数推定が後に続くことがあります)。 これらのステップによって発見された候補関数が2つ以上ある場合は、実際に呼ぶ関数を選択するためにオーバーロード解決が行われます。
一般的には、実引数と最も近くマッチする仮引数を持つ候補関数が選択されます。
他の文脈でオーバーロードされた関数名が現れる場合については、オーバーロードされた関数のアドレスを参照してください。
ある関数がオーバーロード解決によって選択できない場合 (制約が満たされないテンプレート化されたエンティティなど)、その関数は指し示すことも使用することもできません。
目次 |
[編集] 詳細
オーバーロード解決が始まる前に、名前探索によって選択された関数とテンプレートの実引数推定によって選択された関数が組み合わされて候補関数の集合が形成されます (正確な基準はオーバーロード解決が行われた文脈に依存します (後述))。
候補関数がメンバ関数 (静的でも非静的でも) (コンストラクタは除きます) の場合、その関数は、実際の最初の仮引数より前に、呼ばれたオブジェクトを表す追加の仮引数 (暗黙のオブジェクト仮引数) を持つかのように扱われます。
同様に、メンバ関数が呼ばれているオブジェクトが暗黙のオブジェクト実引数として実引数リストの前に追加されます
暗黙のオブジェクト仮引数の型は、メンバ関数で説明されているように、そのメンバ関数の cv 修飾および参照修飾に影響されます。
ユーザ定義変換関数は、暗黙のオブジェクト仮引数の型を決定する目的のためには、暗黙のオブジェクト実引数のメンバであるとみなされます。
using 宣言によって派生クラスに導入されたメンバ関数は、暗黙のオブジェクト仮引数の型を定義する目的のためには、その派生クラスのメンバであるとみなされます。
静的メンバ関数の場合、暗黙のオブジェクト仮引数は、あらゆるオブジェクトにマッチするとみなされます。 その型は調べられず、それに対する変換シーケンスは試みられません。
オーバーロード解決の残りの部分について、暗黙のオブジェクト実引数は他の実引数と区別されませんが、暗黙のオブジェクト仮引数には以下の特別なルールが適用されます。
struct B { void f(int); }; struct A { operator B&(); }; A a; a.B::f(1); // エラー、暗黙のオブジェクト引数には // ユーザ定義変換は適用できません。 static_cast<B&>(a).f(1); // OK。
候補が関数テンプレートの場合は、テンプレートの実引数推定を用いて特殊化が生成され、その特殊化が、タイブレーカールールで規定されているケースを除いて、非テンプレート関数と同様に扱われます。 名前が1つ以上の関数テンプレートを参照しており、さらにオーバーロードされた非テンプレート関数も参照している場合は、それらの関数と、テンプレートから生成された特殊化が、すべて候補になります。
コンストラクタテンプレートまたは変換関数テンプレートが値依存な条件 explicit 指定子を持つ場合、推定後、その文脈が explicit でない候補を要求するけれども、生成された特殊化が explicit であれば、それは候補集合から削除されます。 |
(C++20以上) |
削除されたものとして定義されたデフォルト化されたムーブコンストラクタおよびムーブ代入は、候補関数のリストに含まれることはありません。
継承されたコピーおよびムーブコンストラクタは、派生クラスのオブジェクトを構築するときは、候補関数のリストに含まれません。
[編集] 候補関数
候補関数の集合および実引数のリストは、オーバーロード解決が行われる文脈に応じて、異なる方法で準備されます。
[編集] 名前付きの関数の呼び出し
関数呼び出し式 E(args)
の E
がオーバーロードされた関数の集合または関数テンプレートまたはその両方 (呼び出し可能なオブジェクトは含まれません) を表す場合は、以下のルールに従います。
- 式
E
がPA->B
またはA.B
(ただし A はクラス型 cv T です) の形式を持つ場合は、B
がT
のメンバ関数として名前探索されます。 その名前探索によって発見された関数宣言が候補関数です。 オーバーロード解決用の実引数リストは、型 cv T の暗黙のオブジェクト実引数を持ちます。 - 式
E
が一次式の場合は、その名前が関数呼び出しに対する通常のルールに従って名前探索されます (ADL を発生させることがあります)。 この名前探索によって発見される関数宣言は、 (名前探索の動作方法のために) 以下のいずれかです。
- a) すべて非メンバ関数である。 この場合、オーバーロード解決用の実引数リストは、関数呼び出し式で使用された実引数と同じです。
- b) すべて何らかのクラス
T
のメンバ関数である。 この場合、 this がスコープ内であり、それがT
へのポインタまたはT
の派生クラスへのポインタであれば、*this
が暗黙のオブジェクト実引数として使用されます。 そうでなければ (this
がスコープ内でないかT
を指さなければ)、T
型のフェイクオブジェクトが暗黙のオブジェクト実引数として使用され、後にオーバーロード解決が非静的メンバを選択した場合は、プログラムは ill-formed です。
[編集] クラスオブジェクトの呼び出し
関数呼び出し式 E(args)
の E
がクラス型 cv T
の場合は、
- 式
(E).operator()
の文脈で名前 operator() の通常の名前探索によって T の関数呼び出し演算子が取得され、発見されたすべての宣言が候補関数の集合に追加されます。 -
T
またはT
の基底 (隠蔽されている場合は除きます) の非 explicit なユーザ定義変換関数のうち、その cv 修飾がT
の cv 修飾以上であり、その変換関数の変換先が
- 関数へのポインタ
- 関数へのポインタへの参照
- 関数への参照
- のいずれかであるものそれぞれについて、第1引数がその変換の結果であり、残りの引数がその変換の結果によって受理される引数リストであり、戻り値の型がその変換の結果の戻り値の型である、一意な名前の代理呼び出し関数が、候補関数の集合に追加されます。 この代理関数が後のオーバーロード解決によって選択された場合は、ユーザ定義変換関数が呼ばれ、その後、その変換の結果が呼ばれます。
いずれの場合も、オーバーロード解決用の実引数リストは、関数呼び出し式の実引数リストの前に暗黙のオブジェクト実引数 E
を追加したものです (代理関数に対してマッチングするときは、ユーザ定義変換が暗黙のオブジェクト引数を代理関数の第1引数に自動的に変換します)。
int f1(int); int f2(float); struct A { using fp1 = int(*)(int); operator fp1() { return f1; } // 関数へのポインタへの変換関数。 using fp2 = int(*)(float); operator fp2() { return f2; } // 関数へのポインタへの変換関数。 } a; int i = a(1); // 変換関数から返されたポインタを通して f1 を呼びます。
[編集] オーバーロードされた演算子の呼び出し
式内の演算子の実引数の少なくともひとつがクラス型または列挙型の場合は、組み込みの演算子とユーザ定義の演算子オーバーロードの両方が、以下のように選択される候補関数の集合を用いて、オーバーロード解決に参加します。
実引数が T1
型 (cv 修飾を除去した後) の単項演算子、または左の被演算子が T1
型で右の被演算子が T2
型 (cv 修飾を除去した後) の二項演算子の場合は、以下の候補関数の集合が準備されます。
operator@
の名前探索 (ADL を発生させる場合があります) によって発見されたすべての宣言です。 ただし、メンバ関数の宣言は無視され、名前探索が次の囲っているスコープに出ることを妨げられません。 二項演算子の両方の被演算子、または単項演算子の唯一の演算子が列挙型の場合は、非メンバ候補になる名前探索集合の関数は引数が列挙型 (または列挙への参照型) であるものだけです。
4) 書き換えられた候補。 6つの関係演算子式 x==y、 x!=y、 x<y、 x<=y、 x>y、および x>=y の場合は、 x<=>y @ 0 が well-formed である (つまり、 <=> が std::*_ordering を返し、 @ が ==、 !=、 <、 >、 <=、 >= のいずれかであるか、 <=> が std::*_equality を返し、 @ が ==、 != のいずれかである) ならば、すべてのメンバ、非メンバ、および組み込みの operator<=> の発見されたものが、集合に追加されます。 さらに、6つの関係演算子式 x==y、 x!=y、 x<y、 x<=y、 x>y、 x>=y および三方比較式 x<=>y について、2つの引数の順番を逆にした合成候補が、メンバ、非メンバ、および組み込みの operator<=> の発見されたものそれぞれに対して、 0 @ y <=> x が well-defined であれば、追加されます。 いずれの場合でも、書き換えられた候補は、書き換えられた式の文脈では考慮されません。 それ以外の演算子の場合は、書き換えられた候補の集合は空です。
|
(C++20以上) |
オーバーロード解決用に提示される候補関数の集合は、上記の集合の和です。 オーバーロード解決用の実引数リストは、 operator->
を除いて、その演算子の被演算子から構成されます。 operator->
の場合は、第2被演算子は関数呼び出しの実引数ではありません (メンバアクセス演算子を参照してください)。
struct A { operator int(); // ユーザ定義変換。 }; A operator+(const A&, const A&); // 非メンバのユーザ定義演算子。 void m() { A a, b; a + b; // メンバ候補: なし // 非メンバ候補: operator+(a,b) // 組み込み候補: int(a) + int(b) // オーバーロード解決は operator+(a,b) を選択します。 }
オーバーロード解決が組み込み候補を選択する場合、クラス型の被演算子からのユーザ定義変換シーケンスは、2回目の標準変換シーケンスを行いません。 ユーザ定義変換関数は期待される被演算子の型を直接返さなければなりません。
struct Y { operator int*(); }; // Y は int* に変換可能です。 int *a = Y() + 100.0; // エラー、ポインタと double の operator+ はありません。
operator,、単項 operator&、および operator-> の場合は、候補関数の集合に実行可能関数 (後述) がなければ、その演算子は組み込みのものとして再解釈されます。
この場合のオーバーロード解決は、書き換えられた候補よりも書き換えられていない候補を優先し、合成された書き換えられた候補よりも合成されていない書き換えられた候補を優先する、最終タイブレーカーを持ちます。 逆順の引数を用いるこの名前探索によって、 std::string と const char* の間のすべての比較を生成するために、 operator<=>(std::string, const char*) をひとつだけ書けば良くなります。 詳細については デフォルト比較を参照してください。 |
(C++20以上) |
[編集] コンストラクタによる初期化
クラス型のオブジェクトがコピー初期化以外の文脈で直接初期化またはデフォルト初期化されるときは、候補関数は初期化中のクラスのすべてのコンストラクタです。 実引数リストは初期化子の式リストです。
クラス型のオブジェクトが同じまたは派生クラス型のオブジェクトからコピー初期化される、またはコピー初期化の文脈でデフォルト初期化されるときは、候補関数は初期化中のクラスのすべての変換コンストラクタです。 実引数リストは初期化子の式です。
[編集] 変換によるコピー初期化
クラス型のオブジェクトのコピー初期化で、初期化子の cv S
型の式を、初期化中のオブジェクトの cv T
型に変換するために、ユーザ定義変換関数を呼ぶ必要がある場合は、以下の関数が候補関数です。
-
T
のすべての変換コンストラクタ。 -
S
およびその基底クラス (隠蔽されていなければ) からT
またはその派生クラスまたはそれらの参照への非 explicit な変換関数。 このコピー初期化が cvT
の直接初期化のシーケンスの一部である (cvT
への参照を取るコンストラクタの第1引数に束縛される参照を初期化する) 場合は、 explicit な変換関数も考慮されます。
いずれの場合でも、オーバーロード用の実引数リストは、コンストラクタの第1引数または変換関数の暗黙のオブジェクト引数に対して比較される、初期化子の式1個から構成されます。
[編集] 変換による非クラスの初期化
非クラス型 cv1 T
のオブジェクトの初期化で、クラス型 cv S
の初期化子式から変換するためにユーザ定義変換関数が必要なときは、以下の関数が候補です。
- 型
T
または標準変換シーケンスによってT
に変換可能な型またはそのような型の参照を生成する、S
およびその基底クラス (隠蔽されていなければ) の非 explicit なユーザ定義変換関数。 戻り値の型の cv 修飾は、候補関数を選択する目的のためには無視されます。 - これが直接初期化の場合は、型
T
または修飾変換によってT
に変換可能な型またはそのような型の参照を生成する、S
およびその基底クラス (隠蔽されていなければ) の explicit なユーザ定義変換関数も考慮されます。
いずれの場合でも、オーバーロード解決用の実引数リストは、変換関数の暗黙のオブジェクト引数に対して比較される、初期化子式1個から構成されます。
[編集] 変換による参照初期化
参照初期化で、クラス型 cv2 S
の初期化子式からの変換の結果である左辺値または右辺値に cv1 T
への参照が束縛されるとき、以下の関数が候補集合に選択されます。
-
S
およびその基底クラス (隠蔽されていなければ) の非 explicit な、以下の型へのユーザ定義変換関数。
- (左辺値参照または関数への右辺値参照を初期化するとき) cv2
T2
への左辺値参照。 - (右辺値参照または関数への左辺値参照を初期化するとき) cv2
T2
または cv2T2
への右辺値参照。
- (左辺値参照または関数への右辺値参照を初期化するとき) cv2
- ただし cv2 T2 は cv1 T と参照互換であるとします。
- 直接初期化の場合は、 T2 が T と同じ型であるか、修飾変換で型 T に変換可能であれば、 explicit なユーザ定義変換関数も考慮されます。
いずれの場合でも、オーバーロード解決用の実引数リストは、変換関数の暗黙のオブジェクト引数と比較される、初期化子式1個から構成されます。
[編集] リスト初期化
非集成体クラス型 T
のオブジェクトがリスト初期化されるときは、2フェーズのオーバーロード解決が行われます。
- フェーズ1では、候補関数は
T
のすべての初期化子リストコンストラクタで、オーバーロード解決用の実引数リストは初期化子リスト引数1個から構成されます。 - フェーズ1でオーバーロード解決に失敗すると、フェーズ2に入ります。 フェーズ2では、候補関数は
T
のすべてのコンストラクタで、オーバーロード解決用の実引数リストは初期化子リストの個々の要素から構成されます。
初期化子リストが空で、 T
がデフォルトコンストラクタを持つ場合は、フェーズ1はスキップされます。
コピーリスト初期化では、フェーズ2で explicit コンストラクタが選択された場合、その初期化は ill-formed です (explicit コンストラクタが考慮さえされないコピー初期化とは全面的に異なります)。
[編集] 実行可能関数
上記のように構築された候補関数の集合が与えられると、オーバーロード解決の次のステップは、その集合を実行可能関数の集合に縮小するために、実引数と仮引数を調べます。
実行可能関数の集合に含まれるためには、候補関数は以下の内容を満たさなければなりません。
M
個ある場合は、ちょうど M
個の仮引数を持つ候補関数は実行可能です。M
個より多いけれども、 M+1
番目およびそれ以降のすべての仮引数がデフォルト引数を持つならば、それは実行可能です。 この場合、オーバーロード解決の残りの部分では、仮引数リストを M 個に切り捨てて考えます。
4) 関数に紐付いた制約がある場合は、それが満たされなければなりません。
|
(C++20以上) |
ユーザ定義変換 (変換コンストラクタとユーザ定義変換関数のいずれも) は、それがユーザ定義変換を2回以上適用することを可能にする場合は、暗黙の変換シーケンスに参加することが禁止されます。 具体的には、変換のターゲットが、コンストラクタの第1引数であるか、ユーザ定義変換関数の暗黙のオブジェクト引数であり、そのコンストラクタまたはユーザ定義変換が以下の候補である場合、その変換は考慮されません。
- ユーザ定義変換によるクラスのコピー初期化
- 変換関数による非クラス型の初期化
- 直接参照束縛のための変換関数による初期化
- クラスのコピー初期化の第2ステップ (直接初期化) 中のコンストラクタによる初期化
- 初期化子リストが要素をひとつだけ持ち、それ自体が初期化子リストであり、ターゲットがクラス C のコンストラクタの第1引数であり、その変換の変換先が X または X (cv 修飾されていても構いません) への参照である場合の、リスト初期化による初期化。
struct A { A(int); }; struct B { B(A); }; B b{ {0} }; // B のリスト初期化。 // 候補: B(const B&), B(B&&), B(A) // {0} → B&& ー 実行可能でない。 B(A) を呼ぶ必要があります。 // {0} → const B& ー 実行可能でない。 右辺値に束縛する必要があり、それは B(A) を呼ぶ必要があります。 // {0} → A ー 実行可能。 A(int) を呼びます。 A へのユーザ定義変換は禁止されません。
[編集] 最良実行可能関数
実行可能関数の組 F1
および F2
のそれぞれについて、 i
番目の実引数から i
番目の仮引数への暗黙の変換シーケンスが、どちらが良いかを判断するために、順位付けされます (最初の引数は除きます ー 暗黙のオブジェクト引数は静的メンバ関数に対しては順位付けにおいて効果がありません)。
F1 のすべての実引数に対する暗黙の変換が F2 のすべての実引数に対する暗黙の変換より悪くない、かつ、以下のいずれかを満たすならば、 F1
は F2
より良い関数であると判断されます。
3) (関数型への参照の直接参照束縛のための変換関数による初期化の文脈においてのみ) F1 の戻り値の型が初期化中の参照と同じ種類の参照 (左辺値または右辺値) であり、 F2 の戻り値の型がそうでない。
|
(C++11以上) |
6) F1 と F2 が同じ仮引数型リストを持つ非テンプレート関数であり、制約の半順序に従えば F1 方が F2 より多く制約されている。
|
(C++20以上) |
7) F1 がクラス D のコンストラクタであり、 F2 が D の基底クラス B のコンストラクタであり、すべての実引数について F1 と F2 の対応する仮引数が同じ型である。
|
(C++11以上) |
8) F2 が書き換えられた候補であり、 F1 がそうでない。
9) F1 と F2 がどちらも書き換えられた候補であり、 F2 が逆順の引数を持つ合成された書き換えられた候補であり、 F1 がそうでない。
|
(C++20以上) |
10) F1 がユーザ定義の推定ガイドから生成されたものであり、 F2 がそうでない。
11) F1 がコピー推定候補であり、 F2 がそうでない。
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 A(A) …… コピー推定候補 A x (1, 2, 3); // #3 を使用します (非テンプレートコンストラクタから生成されたもの)。 template <class T> A(T) -> A<T>; // #6 …… #5 ほど特殊化されていない A a (42); // A<int> の推定のために #6 を使用し、初期化のために #1 を使用します。 A b = a; // A<int> の推定のために #5 を使用し、初期化のために #2 を使用します。 template <class T> A(A<T>) -> A<A<T>>; // #7 …… #5 の特殊化として A b2 = a; // A<A<int>> の推定のために #7 を使用し、初期化のために #1 を使用します。 |
(C++17以上) |
すべての実行可能関数について、これらの組ごとの比較が適用されます。 他のすべての実行可能関数より良い実行可能関数がちょうど1個存在するならば、オーバーロード解決は成功し、その関数が呼ばれます。 そうでなければ、コンパイルは失敗します。
void Fcn(const int*, short); // オーバーロード #1 void Fcn(int*, int); // オーバーロード #2 int i; short s = 0; void f() { Fcn(&i, 1L); // 第1引数: &i → int* は &i → const int* より良い。 // 第2引数: 1L → short と 1L → int は同等。 // Fcn(int*, int) が呼ばれます。 Fcn(&i,'c'); // 第1引数: &i → int* は &i → const int* より良い。 // 第2引数: 'c' → int は 'c' → short より良い。 // Fcn(int*, int) が呼ばれます。 Fcn(&i, s); // 第1引数: &i → int* は &i → const int* より良い。 // 第2引数: s → short は s -> int より良い。 // 勝者なし、コンパイルエラー。 }
[編集] 暗黙の変換シーケンスの順位付け
オーバーロード解決によって考慮される実引数から仮引数への暗黙の変換シーケンスは、コピー初期化で使用される暗黙の変換に対応します (非参照の仮引数のみ)。 ただし、暗黙のオブジェクト引数または代入演算子の左側への変換を考慮するときは、一時オブジェクトを作成する変換は考慮されません。
標準変換シーケンスの種類に応じて、以下の3つの順位のいずれかが割り当てられます。
標準変換シーケンスの順位は、それに含まれる標準変換 (最大3回の変換が行われます) のうち最も悪い順位です。
参照仮引数の実引数式への直接の束縛は、恒等または派生から基底への変換のいずれかです。
struct Base {}; struct Derived : Base {} d; int f(Base&); // オーバーロード #1 int f(Derived&); // オーバーロード #2 int i = f(d); // d → Derived& の順位は正確なマッチです。 // d → Base& の順位は変換です。 // f(Derived&) を呼びます。
変換シーケンスの順位は型および値カテゴリにのみ作用するため、ビットフィールドは、順位付けの目的のためには参照実引数に束縛できますが、その関数が選択された場合は ill-formed になります。
S1
は標準変換シーケンス S2
より良いです。S1
が S2
の部分シーケンスである。 恒等変換シーケンスはそれ以外の任意の変換の部分シーケンスであるとみなされます。S1
の順位が S2
の順位より良い。S1
と S2
がどちらも参照修飾されたメンバ関数の暗黙のオブジェクト引数以外の何らかの参照仮引数への束縛であり、 S1
が右辺値参照を右辺値に束縛し、 S2
が左辺値参照を右辺値に束縛する。
int i; int f1(); int g(const int&); // オーバーロード #1 int g(const int&&); // オーバーロード #2 int j = g(i); // 左辺値 int → const int& が唯一の有効な変換です。 int k = g(f1()); // 右辺値 int → const int&& は右辺値 int → const int& より良いです。
S1
と S2
がどちらも参照仮引数への束縛であり、 S1
が左辺値参照を関数に束縛し、 S2
が右辺値参照を関数に束縛する。
int f(void(&)()); // オーバーロード #1 int f(void(&&)()); // オーバーロード #2 void g(); int i1 = f(g); // #1 を呼びます。
S1
と S2
がどちらもトップレベルの cv 修飾が異なるだけの参照仮引数への束縛であり、 S1
の型が S2
の型より少なく cv 修飾されている。
int f(const int &); // オーバーロード #1 int f(int &); // オーバーロード #2 (どちらも参照) int g(const int &); // オーバーロード #1 int g(int); // オーバーロード #2 (参照ではない) int i; int j = f(i); // 左辺値 i → int& は左辺値 int → const int& より良い。 // f(int&) を呼びます。 int k = g(i); // 左辺値 i → const int& の順位は正確なマッチ。 // 左辺値 i → 右辺値 int の順位は正確なマッチ。 // 曖昧なオーバーロード (コンパイルエラー)。
S1
の結果の cv 修飾が S2
の結果の cv 修飾の部分集合である。
int f(const int*); int f(int*); int i; int j = f(&i); // &i → int* は &i → const int* より良い。 f(int*) を呼びます。
U1
とユーザ定義変換シーケンス U2
が同じコンストラクタまたはユーザ定義変換関数を呼ぶまたは同じクラスを集成体初期化で初期化し、 U1
の2回目の標準変換シーケンスが U2
の2回目の標準変換シーケンスより良い場合、 U1
は U2
より良いです。
struct A { operator short(); // ユーザ定義変換関数 } a; int f(int); // オーバーロード #1 int f(float); // オーバーロード #2 int i = f(a); // #1: A → short の後に short → int (順位は昇格) // #2: A → short の後に short → float (順位は変換) // f(int) を呼びます。
L1
が std::initializer_list 仮引数を初期化し、リスト初期化シーケンス L2
がそうでない場合、 L1
は L2
より良いです。
void f1(int); // #1 void f1(std::initializer_list<long>); // #2 void g1() { f1({42}); } // #2 を選択します。 void f2(std::pair<const char*, const char*>); // #3 void f2(std::initializer_list<std::string>); // #4 void g2() { f2({"foo","bar"}); } // #4 を選択します。
6) 対応する仮引数が配列への参照であり、リスト初期化シーケンス
L1 が「T 型 N1 個の配列」型への変換であり、リスト初期化シーケンス L2 が「T 型 N2 個の配列」型への変換であり、 N1 が N2 より小さい場合、 L1 は L2 より良いです。 |
(C++14以上) |
2つの変換シーケンスが同じ順位であるために区別できない場合は、以下の追加のル���ルが適用されます。
(C++11以上) |
Mid
が Base
からの (直接または間接的な) 派生であり、 Derived
が Mid
からの (直接または間接的な) 派生である場合、実引数に対する複数の変換シーケンスは、それらが異なるユーザ定義変換を含む場合にのみ存在できるため、曖昧な変換シーケンスはユーザ定義変換シーケンスとして順位付けされます。
class B; class A { A (B&);}; // 変換コンストラクタ class B { operator A (); }; // ユーザ定義変換関数 class C { C (B&); }; // 変換コンストラクタ void f(A) { } // オーバーロード #1 void f(C) { } // オーバーロード #2 B b; f(b); // #1: コンストラクタを通した B → A または関数を通した B → A (曖昧な変換) // #2: コンストラクタを通した b → C (ユーザ定義変換) // オーバーロード #1 に対する変換とオーバーロード #2 に対する変換は区別できません。 // コンパイルは失敗します。
[編集] リスト初期化における暗黙の変換シーケンス
リスト初期化では、実引数は波括弧初期化子リスト (これは式ではありません) であるため、以下の特別なルールに従って、オーバーロード解決用の仮引数の型への暗黙の変換シーケンスが決定されます。
|
(C++14以上) |
- 仮引数の型が 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}; // #1 を選択します (右辺値 double → double (恒等変換)) void g(A); g({"foo","bar"}); // #3 を選択します (左辺値 const char[4] → std::string (ユーザ定義変換))
- そうでなく、仮引数の型が「T 型 N 個の配列」の場合 (これは配列への参照の場合にのみ発生します)、初期化子リストの要素数は N 個以下でなければならず (C++14以上)、リストのすべての要素 (またはリストが N 個より短い場合は残りの要素については空の波括弧 {}) (C++14以上)から
T
への変換に必要な暗黙の変換の、最も悪いものが使用されます。
typedef int IA[3]; void h(const IA&); h({1,2,3}); // int → int (恒等変換)
- そうでなく、仮引数の型が非集成体クラス型
X
の場合、オーバーロード解決は実引数の初期化子リストから初期化するために X のコンストラクタ C を選択します。
|
(C++14以上) |
- そうでなければ、 (C++14以上)暗黙の変換シーケンスは2回目の標準変換シーケンスが恒等変換であるユーザ定��変換です。
複数のコンストラクタが実行可能であり、そのいずれも他より良くない場合、暗黙の変換シーケンスは曖昧な変換シーケンスです。
struct A { A(std::initializer_list<int>); }; void f(A); struct B { B(int, double); }; void g(B); g({'a','b'}); // g(B(int,double)) を呼びます (ユーザ定義変換)。 // g({1.0, 1,0}); // エラー、 double → int は縮小変換であり、リスト初期化では許されません。 void f(B); // f({'a','b'}); // f(A) と f(B) はどちらもユーザ定義変換です。
- そうでなく、仮引数の型が集成体初期化に従って初期化子リストから初期化できる集成体の場合、暗黙の変換シーケンスは2回目の標準変換シーケンスが恒等変換であるユーザ定義変換です。
struct A { int m1; double m2;}; void f(A); f({'a','b'}); // f(A(int,double)) を呼びます (ユーザ定義変換)。
- そうでなく、仮引数が参照の場合は、参照初期化のルールが適用されます。
struct A { int m1; double m2; }; void f(const A&); f({'a','b'}); // 一時オブジェクトが作成され、 f(A(int,double)) が呼ばれます (ユーザ定義変換)。
- そうでなく、仮引数の型がクラスでなく、初期化子リストの要素が1個の場合、暗黙の変換シーケンスはその要素からその仮引数の型への変換に要求されるものになります。
- そうでなく、仮引数の型がクラス型でなく、初期化子リストが要素を持たない場合、暗黙の変換シーケンスは恒等変換です。
実引数が指示付き初期化子リストの場合は、仮引数が集成体初期化のためのルールに従ってその初期化子リストから初期化できる集成体型の場合のみ、変換が可能であり、この場合、暗黙の変換シーケンスは2回目の標準変換シーケンスが恒等変換であるユーザ定義変換です。 オーバーロード解決の後、集成体のメンバの宣言の順序が選択されたオーバーロードに対してマッチしない場合、その仮引数の初期化は ill-formed です。 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、 #1 を呼びます。 f({.y = 2, .x = 1}, 0); // エラー、 #1 を選択しますが、メンバの順序が // マッチしないため、 a の初期化は失敗します。 g({.x = 1, .y = 2}); // エラー、 #3 と #4 の間で曖昧です。 }
|
(C++20以上) |
[編集] 欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 1601 | C++11 | conversion from enum to its underlying type did not prefer the fixed underlying type | fixed type is preferred to what it promotes to |
CWG 1467 | C++14 | same-type list-initialization of aggregates and arrays was omitted | initialization defined |
CWG 2137 | C++14 | init-list ctors lost to copy ctors when list-initializing X from {X} | non-aggregates consider init-lists first |
[編集] 関連項目
[編集] 参考文献
- C++20 standard (ISO/IEC 14882:2020):
- 12.4 Overload resolution [over.match]
- C++17 standard (ISO/IEC 14882:2017):
- 16.3 Overload resolution [over.match]
- C++14 standard (ISO/IEC 14882:2014):
- 13.3 Overload resolution [over.match]
- C++11 standard (ISO/IEC 14882:2011):
- 13.3 Overload resolution [over.match]
- C++03 standard (ISO/IEC 14882:2003):
- 13.3 Overload resolution [over.match]