標準語をふんだんに使ったプリアンブル
この例の への呼び出しにswap()
は、その引数begin[0]
と周囲の関数テンプレートbegin[1]
のテンプレート パラメーターに依存するため、従属名が必要です。このような従属名の 2 段階の名前検索は、標準で次のように定義されています。T
algorithm()
14.6.4.2 候補関数 [temp.dep.candidate]
1 後置式が従属名である関数呼び出しの場合、次の点を除いて、通常のルックアップ規則 (3.4.1、3.4.2) を使用して候補関数が検出されます。
— 非修飾名ルックアップ (3.4.1) を使用したルックアップの部分では、テンプレート定義コンテキストからの関数宣言のみが検出されます。
— 関連付けられた名前空間 (3.4.2) を使用したルックアップの部分では、テンプレート定義コンテキストまたはテンプレート インスタンス化コンテキストのいずれかにある関数宣言のみが検出されます。
非修飾ルックアップは、によって定義されます
3.4.1 非修飾名ルックアップ [basic.lookup.unqual]
1 3.4.1 にリストされているすべてのケースで、スコープは、それぞれのカテゴリのそれぞれにリストされている順序で宣言を検索されます。
名前の検索は、名前の宣言が見つかるとすぐに終了します。宣言が見つからない場合、プログラムは不正な形式です。
および引数依存ルックアップ (ADL) として
3.4.2 引数依存の名前検索 [basic.lookup.argdep]
1 関数呼び出し (5.2.2) の postfix-expression が unqualified -idの場合、通常の非修飾ルックアップ (3.4.1) では考慮されない他の名前空間が検索される可能性があり、それらの名前空間では、名前空間スコープのフレンド関数または他の方法では表示されない関数テンプレート宣言 (11.3) が見つかる場合があります。検索に対するこれらの変更は、引数のタイプ(およびテンプレート テンプレート引数の場合は、テンプレート引数の名前空間)によって異なります。
例への標準の適用
最初の例は を呼び出しますexp::swap()
。これは従属名ではなく、2 フェーズの名前ルックアップを必要としません。swap(T&, T&)
swap の呼び出しは修飾されているため、一般的な関数テンプレートのみを検索する通常のルックアップが行われます。
2 番目の例(@HowardHinnant が「最新のソリューション」と呼んでいるもの) は、呼び出し元と同じ名前空間(この場合はグローバル名前空間) にswap()
オーバーロードがあります。swap の呼び出しは修飾されていないため、通常のルックアップと ADL の両方が定義の時点で行われますが (ここでも generic を見つけるだけです)、インスタンス化の時点で別の ADL が行われます (つまり、が呼び出されている場所) 。オーバーロードの解決中はより適切に一致します。swap(A&, A&)
class A
swap(T&, T&)
exp::algorithm()
main()
swap(A&, A&)
ここまでは順調ですね。アンコール: 3 番目の例では、 が呼び出され、内部swap()
に特殊化が含まれています。ルックアップは 2 番目の例と同じですが、ADL はテンプレートの特殊化を取得しません。関連付けられた名前空間にないためです。ただし、特殊化はオーバーロードの解決中には役割を果たしませんが、使用時にインスタンス化されます。template<> swap(A&, A&)
namespace exp
class A
template<> swap(A&, A&)
最後に、4 番目の例では、グローバル名前空間に存在するためswap()
のオーバーロードを呼び出し、template<class T> swap(A<T>&, A<T>&)
内部namespace exp
に持っています。ルックアップは 3 番目の例と同じですが、クラス template の関連付けられた名前空間にないためtemplate<class T> class A
、ADL はオーバーロードを取得しません。この場合、使用時にインスタンス化する必要がある特殊化も存在しないため、ジェネリックがここで呼び出されます。swap(A<T>&, A<T>&)
A<T>
swap(T&, T&)
結論
に新しいオーバーロードを追加することは許可されておらずnamespace std
、明示的な特殊化のみを追加することは許可されていませんが、2 フェーズの名前検索のさまざまな複雑さのために機能しません。