を宣言したテンプレートコンテキストから非常に奇妙なオーバーロードの解決のミニマリストの例である:C++テンプレートのオーバーロードの解決には、テンプレートのインスタンス化の後にここで
#include <iostream>
// Types //
struct I { int v; };
template <class T>
struct D { T t; };
// Functions //
// Overload 1
template <class T>
I f(T) { return {1}; }
// Template indirection that calls f(T)
template <class T>
I g(D<T>) { return f(T{}); }
// Non template indirection that calls f(T)
I h(D<I>) { return f(I{}); }
int main() {
std::cout
<< f(I{}).v // f(I{}) overload called directly
<< "\n" // => overload 1 called
<< h(D<I>{}).v // f(I{}) called though non-template
<< "\n" // => overload 1 called
<< g(D<I>{}).v // f(I{}) called though template
<< "\n"; // => overload 2 actually called ???
}
// Overload 2
// Should not be reachable as declared after all f(...) calls.
// If was a candidate, would be chosen over 1.
I f(I) { return {2}; }
これはI
場合は理由ADLに関連すると思われます名前空間に置かれる "overload 1"は常に呼び出されます。
私が知っているのは、呼び出しがテンプレートのインスタンス化ポイント(main
)から行われたかのようにADLが実行されていることです。
テンプレート定義で使用される依存名の場合、テンプレート引数がわかるまでルックアップが延期されます。その時点で、ADLはテンプレートから見える外部リンケージ(C++ 11まで)で関数宣言を調べます。非ADLルックアップはテンプレート定義コンテキストから見える外部リンケージ(C++ 11まで)で関数宣言のみを調べます。 http://en.cppreference.com/w/cpp/language/unqualified_lookup#Template_definition
しかし、ここでは "過負荷2は" main
後に宣言されました! main
は、g
とf
のインスタンスポイントであるため、と宣言された関数のみが、main
の前にオーバーロード候補と想定されていました。
g
はテンプレートであり、h
(g
の同等のテンプレート関数ではない)が "Overload 1"を呼び出すことに関連していることに注意してください。
「オーバーロード2」は、main
の後で宣言されました。
この動作は、clang ++(3.8.1)およびg ++(6.2.1)で再現されています。
これは、ライブラリの名前空間にオーバーロードを追加することに対して、もう一つの欠点を追加します。そのような追加は、このオーバーロードがライブラリコードで選択された場合、未定義の動作につながる可能性があります。その合理的ではなく、ルールを知っていた。 –