Overload resolution
関数呼び出しをコンパイルするためには、コンパイラはまず name lookup を実行する必要があります。関数の場合、これは argument-dependent lookup を含む場合があり、関数テンプレートの場合には template argument deduction が続くことがあります。
名前が複数のエンティティを参照する場合、それは オーバーロード されていると言われ、コンパイラはどのオーバーロードを呼び出すかを決定しなければなりません。簡単に言えば、パラメータが引数に最も一致するオーバーロードが呼び出されます。
詳細には、オーバーロード解決は以下の手順で進行します:
- 候補関数 の集合を構築する。
- 集合を 実現可能関数 のみに絞り込む。
- 単一の 最良の実現可能関数 を決定するために集合を分析する(これには 暗黙の変換シーケンスのランキング が含まれる場合がある)。
void f(long); void f(float); f(0L); // f(long)を呼び出す f(0); // エラー: オーバーロードの曖昧性
関数呼び出し以外にも、オーバーロードされた関数名はいくつかの追加の文脈で現れる可能性があり、異なる規則が適用されます。詳細は オーバーロードされた関数のアドレス を参照してください。
関数がオーバーロード解決によって選択できない場合、その関数は使用できません(例えば、失敗した templated entity を持つ constraint の場合など)。
目次 |
候補関数
オーバーロード解決が開始される前に、名前探索とテンプレート引数推論によって選択された関数は、 候補関数 の集合を形成するために結合されます。詳細は、オーバーロード解決が行われるコンテキストによって異なります。
名前付き関数の呼び出し
E が 関数呼び出し式 E ( args ) において、オーバーロードされた関数および/または関数テンプレートの集合(ただし呼び出し可能オブジェクトを除く)を指す場合、以下の規則が適用されます:
-
式
E
が
PA
-
>
B
または
A.
B
の形式を持つ場合(ここで
A
はクラス型
cv
Tを持つ)、 B はTのメンバ関数として ルックアップ されます。このルックアップで見つかった関数宣言が候補関数となります。オーバーロード解決のための引数リストは、型 cvTの暗黙のオブジェクト引数を持ちます。 - 式 E が 基本式 である場合、名前は関数呼び出しの通常のルール( ADL を含む可能性あり)に従って ルックアップ されます。このルックアップで見つかった関数宣言は(ルックアップの仕組みにより)以下のいずれかとなります:
-
- すべての非メンバ関数(この場合、オーバーロード解決のための引数リストは関数呼び出し式で使用される引数リストと完全に一致する)
-
あるクラス
Tのすべてのメンバ関数。この場合、 this がスコープ内にあり、TまたはTの派生クラスへのポインタである場合、 * this が暗黙のオブジェクト引数として使用される。それ以外の場合( this がスコープ内にないか、Tを指していない場合)、型Tの仮想オブジェクトが暗黙のオブジェクト引数として使用され、オーバーロード解決の結果非静的メンバ関数が選択された場合、プログラムは不適格となる。
クラスオブジェクトへの呼び出し
E
が
関数呼び出し式
E
(
args
)
においてクラス型
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; } // 関数ポインタへの変換関数 using fp2 = int(*)(float); operator fp2() { return f2; } // 関数ポインタへの変換関数 } a; int i = a(1); // 変換関数から返されたポインタ経由でf1を呼び出す
オーバーロードされた演算子の呼び出し
式内の演算子の引数の少なくとも1つがクラス型または列挙型を持つ場合、 組み込み演算子 と ユーザー定義演算子オーバーロード の両方がオーバーロード解決に参加し、候補関数のセットは以下のように選択されます:
単項演算子
@
で引数の型が
T1
(CV修飾子を除去後)、または二項演算子
@
で左オペランドの型が
T1
、右オペランドの型が
T2
(CV修飾子を除去後)の場合、以下の候補関数群が準備されます:
T1
が完全なクラスまたは現在定義中のクラスである場合、メンバー候補の集合は
qualified name lookup
による
T1::operator@
の検索結果である。それ以外の場合、メンバー候補の集合は空である。
operator@
のすべての宣言(
ADL
を含む可能性がある)が対象となります。ただし、メンバー関数の宣言は無視され、次の外側のスコープへのルックアップの継続を妨げません。二項演算子の両オペランド、または単項演算子の唯一のオペランドが列挙型を持つ場合、ルックアップセットから非メンバー候補となる関数は、そのパラメータがその列挙型(またはその列挙型への参照)を持つもののみです。
|
4)
書き換え候補
:
すべての場合において、書き換え候補は書き換えられた式の文脈では考慮されない。他のすべての演算子に対しては、書き換え候補集合は空である。
|
(C++20以降) |
オーバーロード解決のために提出される候補関数の集合は、上記の集合の和集合です。オーバーロード解決のための引数リストは、演算子のオペランドで構成されます。ただし
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) を選択 }
オーバーロード解決が組み込み候補を選択した場合、クラス型のオペランドからの ユーザー定義変換シーケンス は第二の標準変換シーケンスを持つことが許可されません:ユーザー定義変換関数は直接期待されるオペランド型を提供しなければなりません:
struct Y { operator int*(); }; // Y は int* に変換可能 int *a = Y() + 100.0; // エラー: ポインタと double の間には operator+ が存在しない
operator, 演算子、 operator & 演算子、および operator - > 演算子について、候補関数のセットに有効な関数(下記参照)が存在しない場合、その演算子は組み込み演算子として再解釈されます。
|
書き換えられた
operator
<=>
候補が演算子
書き換えられた
operator
==
候補が演算子
この場合のオーバーロード解決では、最終的なタイブレーカーとして、書き換えられていない候補を書き換えられた候補よりも優先し、合成されていない書き換えられた候補を合成された書き換えられた候補よりも優先する。 この逆順の引数によるルックアップにより、 operator <=> ( std:: string , const char * ) と operator == ( std:: string , const char * ) を定義するだけで、 std::string と const char * の間のすべての比較を両方向で生成することが可能となる。詳細は デフォルト比較 を参照。 |
(C++20以降) |
コンストラクタによる初期化
クラス型のオブジェクトが direct-initialized または default-initialized ( copy-list-initialization におけるdefault-initializationを含む) (C++11以降) される場合、候補関数は初期化されるクラスの全てのコンストラクタです。引数リストは初期化子の式リストとなります。
それ以外の場合、候補関数はすべて初期化されるクラスの converting constructors です。引数リストは初期化子の式となります。
|
コピーリスト初期化の文脈におけるデフォルト初期化では、
|
(C++11以降) |
変換によるコピー初期化
クラス型のオブジェクトの
copy-initialization
において、型
cv
S
の初期化子式を初期化対象オブジェクトの型
cv
T
に変換するためにユーザー定義変換の呼び出しが必要な場合、以下の関数が候補関数となります:
-
Tのすべての 変換コンストラクタ -
Sとその基底クラスからT、またはTから派生したクラス、あるいはそれらへの参照へのexplicitでない変換関数(隠蔽されていないもの)。このコピー初期化が cvTの直接初期化シーケンスの一部である場合( cvTへの参照を取るコンストラクタの最初の引数にバインドされる参照を初期化する場合)、明示的変換関数も考慮されます。
いずれにせよ、オーバーロード解決のための引数リストは、初期化式である単一の引数で構成され、これはコンストラクタの第一引数、または変換関数の暗黙のオブジェクト引数と比較されます。
変換による非クラス初期化
非クラス型
cv1
T
のオブジェクトの初期化が、クラス型
cv
S
の初期化子式からの変換のために
ユーザー定義変換関数
を必要とする場合、以下の関数が候補となります:
-
Sとその基底クラスの非明示的ユーザー定義変換関数(隠蔽されていないもの)で、型Tまたは 標準変換シーケンス によってTに変換可能な型(またはそのような型への参照)を生成するもの。候補関数の選択においては、返される型のcv修飾子は無視されます。 -
これが
直接初期化
の場合、
Sとその基底クラスの明示的ユーザー定義変換関数(隠蔽されていないもの)で、型Tまたは 修飾変換 によってTに変換可能な型(またはそのような型への参照)を生成するものも考慮されます。
いずれにせよ、オーバーロード解決のための引数リストは、初期化式である単一の引数で構成され、これは変換関数の暗黙のオブジェクト引数と比較されます。
変換による参照初期化
参照初期化
において、
cv1
T
への参照がクラス型
cv2
S
からの初期化子式の変換結果である左辺値または右辺値に束縛される場合、以下の関数が候補集合として選択されます:
-
Sとその基底クラス(隠されていない限り)の非明示的なユーザー定義変換関数が、型への変換を行う場合
-
-
(左辺値に変換する場合)
cv2
T2への左辺値参照 -
(右辺値または関数型の左辺値に変換する場合)
cv2
T2または cv2T2への右辺値参照
-
(左辺値に変換する場合)
cv2
-
ここで
cv2
T2は 参照互換 である cv1Tと。
-
直接初期化の場合、明示的なユーザー定義変換関数も考慮されます。ただし、
T2がTと同じ型であるか、あるいは修飾変換によって型Tに変換可能な場合に限ります。
いずれにせよ、オーバーロード解決のための引数リストは、初期化式である単一の引数で構成され、これは変換関数の暗黙のオブジェクト引数と比較されます。
リスト初期化
非集成クラス型
T
のオブジェクトが
list-initialized
される場合、2段階のオーバーロード解決が行われます。
-
フェーズ1では、候補関数は
Tの全ての初期化子リストコンストラクタであり、オーバーロード解決のための引数リストは単一の初期化子リスト引数で構成されます -
フェーズ1でオーバーロード解決が失敗した場合、フェーズ2に移行します。このフェーズでは候補関数は
Tの全てのコンストラクタであり、オーバーロード解決のための引数リストは初期化子リストの個々の要素で構成されます
初期化子リストが空であり、かつ
T
がデフォルトコンストラクタを持つ場合、フェーズ1はスキップされます。
コピーリスト初期化では、フェーズ2が明示的コンストラクタを選択した場合、初期化は不適格となる(他のすべてのコピー初期化では明示的コンストラクタは考慮すらされないのとは対照的である)。
関数テンプレート候補に関する追加規則
名前探索が関数テンプレートを見つけた場合、 template argument deduction および明示的なテンプレート引数のチェックが実行され、このケースで使用可能なテンプレート引数値(存在する場合)が決定されます:
- 両方が成功した場合、テンプレート引数は対応する関数テンプレート特殊化の宣言を合成するために使用され、それらは候補セットに追加されます。このような特殊化は、以下のタイブレイカー規則で特に指定されている場合を除き、非テンプレート関数と同様に扱われます。
- 引数推定が失敗した場合、または合成された関数テンプレート特殊化が不適格となる場合、そのような関数は候補セットに追加されません( SFINAE を参照)。
名前が1つ以上の関数テンプレートを参照し、かつ一連のオーバーロードされた非テンプレート関数も参照する場合、それらの関数とテンプレートから生成された特殊化はすべて候補となります。
詳細については、 function template overloading を参照してください。
|
コンストラクタテンプレートまたは変換関数テンプレートが、 条件付きexplicit指定子 を持ち、それがたまたま 値に依存する 場合、推論後、非explicitな候補が必要なコンテキストで生成された特殊化がexplicitである場合、その候補は候補集合から除去される。 |
(C++20以降) |
コンストラクタ候補に関する追加規則
|
削除済みとして定義されたデフォルト化された move constructors および move assignment operators は、候補関数の集合から除外されます。
クラス型
|
(C++11以降) |
メンバー関数候補に関する追加規則
候補関数のいずれかが メンバ関数 (静的または非静的)であり、 明示的なオブジェクトパラメータ を持たない場合 (C++23以降) 、ただしコンストラクタではない場合、その関数は呼び出し対象のオブジェクトを表す追加のパラメータ( 暗黙のオブジェクトパラメータ )を持っているかのように扱われ、このパラメータは実際のパラメータの最初の前に現れます。
同様に、メンバー関数が呼び出されているオブジェクトは、 implied object argument として引数リストの先頭に追加されます。
クラス
X
のメンバ関数について、暗黙のオブジェクトパラメータの型は、
member functions
で説明されているように、メンバ関数のCV修飾と参照修飾の影響を受けます。
ユーザー定義変換関数は、 implicit object parameter の型を決定する目的において、 implied object argument のメンバーと見なされます。
using宣言によって派生クラスに導入されたメンバ関数は、 暗黙の オブジェクトパラメータの型を定義する目的において、派生クラスのメンバと見なされます 。
|
静的メンバ関数の場合、 implicit object parameter は任意のオブジェクトに一致すると見なされます:その型は検査されず、変換シーケンスも試行されません。 |
(C++23まで) |
残りのオーバーロード解決において、 implied object argument は他の引数と区別できませんが、以下の特別な規則が implicit object parameter に適用されます:
struct B { void f(int); }; struct A { operator B&(); }; A a; a.B::f(1); // エラー: ユーザー定義変換は暗黙オブジェクトパラメータに適用できない // to the implicit object parameter static_cast<B&>(a).f(1); // OK
有効な関数
候補関数の集合が上記のように構築された後、オーバーロード解決の次のステップは、引数とパラメータを調べて、それを 実行可能関数 の集合に絞り込むことです。
有効な関数のセットに含まれるためには、候補関数は以下を満たさなければなりません:
M
個ある場合、正確に
M
個のパラメータを持つ候補関数は有効です
M
個より多いパラメータを持ち、かつ
M+1
番目以降のすべてのパラメータがデフォルト引数を持つ場合、その関数は有効です。オーバーロード解決の残りの処理では、パラメータリストは
M
番目で切り詰められます。
|
4)
関数が関連付けられた
制約
を持つ場合、それは満たされなければならない
|
(C++20以降) |
ユーザー定義変換(変換コンストラクタとユーザー定義変換関数の両方)は、複数のユーザー定義変換を適用可能にする暗黙変換シーケンスに参加することが禁止されています。具体的には、変換の対象がコンストラクタの第一引数、またはユーザー定義変換関数の暗黙オブジェクトパラメータである場合、そのコンストラクタ/ユーザー定義変換が候補となる場合には考慮されません。
- ユーザー定義変換によるクラスのコピー初期化 ,
- 変換関数による非クラス型の初期化 ,
- 直接参照バインドのための変換関数による初期化 ,
- クラスの コピー初期化 の第二段階(直接初期化)におけるコンストラクタによる初期化 ,
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へのユーザー定義変換は禁止されていない |
(C++11以降) |
最適な実行可能関数
有効な関数の各ペア
F1
と
F2
について、
i
番目の
引数から
i
番目の
パラメータへの暗黙変換シーケンスは、どちらが優れているかを判断するためにランク付けされます(ただし、最初の引数である静的メンバ関数の
暗黙オブジェクト引数
はランキングに影響しません)
F1
は、以下の条件を満たす場合に
F2
よりも優れた関数であると判定されます:
F1
のすべての引数に対する暗黙変換が
F2
のすべての引数に対する暗黙変換よりも
劣っていない
こと、かつ
F1
の引数について、その暗黙変換が
F2
の対応する引数の暗黙変換よりも
優れている
場合、あるいは、そうでない場合は、
F1
の結果から初期化される型への標準変換シーケンスが、
F2
の結果からの標準変換シーケンスより
優れている
場合、あるいはそれ以外の場合、
|
3)
(関数型への参照の直接参照バインディングのための変換関数による初期化の文脈でのみ)、
F1
の結果は初期化される参照と同じ種類の参照(左辺値または右辺値)であり、
F2
の結果はそうではない場合、あるいはそうでない場合、
|
(C++11以降) |
F1
は非テンプレート関数である一方、
F2
はテンプレート特殊化、あるいはそうでない場合、
|
6)
F1
と
F2
が非テンプレート関数であり、
F1
が
より部分順序制約されている
場合:
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); // オーバーロード #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 より優れている // 勝者が決まらないため、コンパイルエラー }
最適な実行可能関数が複数の宣言が見つかった関数に解決され、かつこれらの宣言のいずれか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、デフォルト引数は有効性の判定には使用されなかった f(); // エラー: デフォルト引数が2回見つかりました }
暗黙の変換シーケンスのランキング
オーバーロード解決で考慮される引数-パラメータの暗黙変換シーケンスは、 暗黙変換 が コピー初期化 (非参照パラメータの場合)で使用されるものに対応します。ただし、暗黙オブジェクトパラメータまたは代入演算子の左辺への変換を考慮する際には、一時オブジェクトを作成する変換は考慮されません。 パラメータが静的メンバ関数の暗黙オブジェクトパラメータである場合、暗黙変換シーケンスは他の標準変換シーケンスよりも優劣のない標準変換シーケンスです。 (C++23以降)
各 標準変換シーケンスの型 には、次の3つのランクのいずれかが割り当てられます:
標準変換シーケンスのランクは、それが保持する標準変換のランクの中で最も低いものとなる(最大で 3つの変換 が含まれる可能性がある)
参照パラメータの引数式への直接バインディングは、同一性または派生から基底への変換のいずれかです:
struct Base {}; struct Derived : Base {} d; int f(Base&); // オーバーロード #1 int f(Derived&); // オーバーロード #2 int i = f(d); // d -> Derived& は Exact Match ランク // d -> Base& は Conversion ランク // f(Derived&) を呼び出す
変換シーケンスのランキングは型と値カテゴリのみを対象として動作するため、 ビットフィールド はランキングの目的で参照引数に束縛されることがありますが、その関数が選択された場合、プログラムは不適格となります。
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
and
S2
are binding to a reference parameter and
S1
binds an lvalue reference to function while
S2
binds an rvalue reference to function:
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* は &i -> const int* より優先され、f(int*) を呼び出す
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
および
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
は、同じコンストラクタ/ユーザー定義変換関数を呼び出すか、または同じクラスを集成体初期化で初期化する場合において、かつ
U1
の第2標準変換シーケンスが
U2
の第2標準変換シーケンスより
優れている
場合に、ユーザー定義変換シーケンス
U2
より優れているとみなされる
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 (&&)[] ); // オーバーロード #1 void f(double (&&)[] ); // オーバーロード #2 void f(int (&&)[2]); // オーバーロード #3 f({1}); // #1: 変換により#2より優れ、境界により#3より優れる f({1.0}); // #2: double -> double は double -> int より優れる f({1.0, 2.0}); // #2: double -> double は double -> int より優れる f({1, 2}); // #3: -> int[2] は -> int[] より優れ、 // かつ int -> int は int -> double より優れる |
(C++20以降) |
2つの変換シーケンスが同じランクを持つために区別できない場合、以下の追加ルールが適用されます:
|
2)
基盤型が固定された列挙型をその基盤型へ昇格させる変換は、2つの型が異なる場合、昇格された基盤型への変換よりも優先されます。
enum num : char { one = '0' }; std::cout << num::one; // '0', not 48 |
(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); // float と std::float32_t の変換ランクが等しい実装では // f(std::float32_t) を呼び出す int j = f(y); // エラー: 曖昧、等しい変換ランクがない |
(C++23以降) |
Mid
が(直接または間接的に)
Base
から派生しており、かつ
Derived
が(直接または間接的に)
Mid
から派生している場合
Derived
to
Mid
&
or
Mid
&&
is better than
Derived
to
Base
&
or
Base
&&
Derived
から
Mid
への変換は、
Derived
から
Base
への変換よりも適切です
Mid
から
Base
&
または
Base
&&
への変換は、
Derived
から
Base
&
または
Base
&&
への変換よりも優れている
Mid
から
Base
への変換は、
Derived
から
Base
への変換よりも優れている
曖昧な変換シーケンスはユーザー定義変換シーケンスとしてランク付けされます。なぜなら、引数に対する複数の変換シーケンスが存在し得るのは、それらが異なるユーザー定義変換を含む場合のみだからです:
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); // B -> A 変換(コンストラクタ経由)または B -> A 変換(関数経由)(曖昧な変換) // b -> C 変換(コンストラクタ経由)(ユーザー定義変換) // オーバーロード #1 とオーバーロード #2 の変換は区別できないため、コンパイルは失敗する
リスト初期化における暗黙変換シーケンス
list initialization において、引数は braced-init-list であり、これは式ではないため、オーバーロード解決の目的でパラメータ型への暗黙変換シーケンスは以下の特別な規則によって決定されます:
-
パラメータの型が何らかの集約型
Xであり、初期化子リストが同じクラスまたは派生クラス(CV修飾されている可能性あり)のちょうど1つの要素で構成されている場合、暗黙変換シーケンスは要素をパラメータ型に変換するために必要なものです。 - それ以外の場合、パラメータの型が文字配列への参照であり、初期化子リストが適切な型の文字列リテラルである単一の要素を持つ場合、暗黙変換シーケンスは恒等変換です。
-
それ以外の場合、パラメータの型が
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を選択 (rvalue double → double: identity conversion) void g(A); g({"foo", "bar"}); // #3を選択 (lvalue const char[4] → std::string: user-defined conversion)
-
それ以外の場合、パラメータ型が「N個のTの配列」(これは配列への参照でのみ発生する)の場合、初期化子リストはN個以下の要素を持たなければならず、リストの各要素(またはリストがNより短い場合は空の中括弧のペア
{})をTに変換するために必要な最悪の暗黙変換が使用されます。
|
(C++20以降) |
typedef int IA[3]; void h(const IA&); void g(int (&&)[]); h({1, 2, 3}); // int→int 同一性変換 g({1, 2, 3}); // C++20以降は上記と同じ
-
それ以外の場合、パラメータ型が非集約クラス型
Xである場合、オーバーロード解決は引数初期化子リストから初期化するためにXのコンストラクタCを選択します
-
- Cが初期化子リストコンストラクタでなく、初期化子リストが単一の要素(修飾の有無を問わずX型)を持つ場合、暗黙変換シーケンスはExact Matchランクを持つ。初期化子リストが単一の要素(修飾の有無を問わずXから派生した型)を持つ場合、暗黙変換シーケンスはConversionランクを持つ。(アグリゲートとの違いに注意:アグリゲートは アグリゲート初期化 を考慮する前に単一要素の初期化子リストから直接初期化するが、非アグリゲートは他のコンストラクタよりも前にinitializer_listコンストラクタを考慮する)
- それ以外の場合、暗黙変換シーケンスはユーザー定義変換シーケンスであり、第二標準変換シーケンスは恒等変換である。
複数のコンストラクタが利用可能だが、いずれも他より優れていない場合、暗黙の変換シーケンスはあいまいな変換シーケンスとなります。
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)の両方がユーザー定義変換
- それ以外の場合、パラメータの型が初期化子リストから aggregate initialization によって初期化可能な集成体である場合、暗黙変換シーケンスはユーザー定義変換シーケンスであり、第二標準変換シーケンスは恒等変換となります。
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つの要素を持つ場合、暗黙変換シーケンスは要素をパラメータ型に変換するために必要なものとなります
- それ以外の場合、パラメータ型がクラス型でなく、初期化子リストに要素がない場合、暗黙変換シーケンスは恒等変換となります
|
引数が指示付き初期化子リストであり、パラメータが参照でない場合、変換はパラメータが 集成体初期化 の規則に従ってその初期化子リストから初期化可能な集成体型を持つ場合にのみ可能であり、その場合、暗黙の変換シーケンスは第二標準変換シーケンスが恒等変換であるユーザー定義変換シーケンスとなる。 オーバーロード解決後、選択されたオーバーロードに対して集成体のメンバー宣言順序が一致しない場合、パラメータの初期化は不適格となる。 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
によって指定されるオーバーロード集合が
&F(args)
の場合に非静的メンバ関数を含むと
無効であった |
この場合にオーバーロード解決が
非静的メンバ関数を選択した場合にのみ 無効となる |
| CWG 233 | C++98 |
参照とポインタがユーザー定義変換におけるオーバーロード解決で
一貫性なく扱われていた |
一貫して
扱われるようになった |
| CWG 280 | C++98 |
サロゲート呼び出し関数がアクセス不可能な基底クラスで
宣言された変換関数の候補関数セットに追加されなかった |
アクセシビリティ制約を削除し、
サロゲート呼び出し関数が選択され、 対応する変換関数を呼び出せない場合、 プログラムは不適格となる |
| CWG 415 | C++98 |
関数テンプレートが候補として選択された場合、
その特殊化はテンプレート引数推論を使用して インスタンス化されていた |
この場合、インスタンス化は発生せず、
それらの宣言が合成される |
| CWG 495 | C++98 |
引数の暗黙変換が同等に
適切である場合、非テンプレート変換関数は常に 変換関数テンプレートよりも優先されていた(後者が より良い標準変換シーケンスを持つ場合でも) |
特殊化レベルを比較する前に
標準変換シーケンスが 比較される |
| CWG 1307 | C++11 | 配列のサイズに基づくオーバーロード解決が規定されていなかった |
可能な場合はより短い配列が
優先される |
| CWG 1328 | C++11 |
参照を変換結果にバインドする際の
候補関数の決定が不明確であった |
明確化された |
| CWG 1374 | C++98 |
修飾変換のチェックが参照束縛よりも先に行われていた
標準変換シーケンスの比較時 |
逆順に変更 |
| CWG 1385 | C++11 |
リファレンス修飾子で宣言された非明示的なユーザー定義変換関数は
対応するサロゲート関数を持たなかった |
対応する
サロゲート関数を持つ |
| CWG 1467 | C++11 |
集約型と配列の同一型リスト初期化が
省略されていた |
初期化が定義された |
| CWG 1601 | C++11 |
列挙型からその基底型への変換
固定基底型が優先されなかった |
固定型が優先される
昇格先に対して |
| 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パラメータは一時オブジェクトにバインドできなかったが、 これは既に不可能であった [1] |
冗長な要件を
削除 |
| CWG 2713 | C++20 |
指定初期化子リストに関する変換制限は、パラメータが参照の場合でも適用されていた
|
このケースでは制限されない |
| CWG 2789 | C++23 |
明示的オブジェクトパラメータが含まれるようになった
パラメータ型リストの比較時 |
除外 |
| CWG 2856 | C++11 |
コピーリスト初期化の文脈におけるデフォルト初期化のオーバーロード解決は
変換コンストラクタのみを考慮していた |
すべてのコンストラクタを考慮する |
| CWG 2919 | C++98 |
変換による参照初期化の候補集合は
初期化のターゲット型に依存していた |
変換のターゲット型に
依存する |
| P2468R2 | C++20 |
operator
==
に基づく書き換え候補が追加される
a ! = b に対して、一致する operator ! = が存在する場合でも |
追加されない |
-
↑
組み込み代入演算子の最初のパラメータの型は「volatile修飾された可能性のある
Tへの左辺値参照」です。この型の参照は一時オブジェクトに束縛できません。
参考文献
- C++23標準 (ISO/IEC 14882:2024):
-
- 12.2 オーバーロード解決 [over.match]
- C++20標準 (ISO/IEC 14882:2020):
-
- 12.4 オーバーロード解決 [over.match]
- C++17標準 (ISO/IEC 14882:2017):
-
- 16.3 オーバーロード解決 [over.match]
- C++14標準 (ISO/IEC 14882:2014):
-
- 13.3 オーバーロード解決 [over.match]
- C++11規格 (ISO/IEC 14882:2011):
-
- 13.3 オーバーロード解決 [over.match]
- C++03規格 (ISO/IEC 14882:2003):
-
- 13.3 オーバーロード解決 [over.match]