次のオーバーロード/特殊化動作に対して正しいコンパイラはどれですか?
-
03-07-2019 - |
質問
次のコードを検討してください:
#include <stdio.h>
namespace Foo {
template <typename T>
void foo(T *, int) { puts("T"); }
template <typename T>
struct foo_fun {
static void fun() { foo((T *)0, 0); };
};
}
namespace Foo {
void foo(int *, int) { puts("int"); }
}
using namespace Foo;
int main() {
foo_fun<int> fun;
fun.fun();
}
期待される出力は何ですか? &quot; T&quot;またはint?
1つのコンパイラ(AppleのXcode 3.1.2のgcc 4.0.1)が&quot; int&quot;を出力し、他の2つのコンパイラ(gcc 4.1.2と4.1.3)が&quot; T&quot;を出力します。
foo(T *、int)バージョンの前にfoo(int *、int)宣言/定義を移動すると、すべての出力が&quot; int&quot;になります。この場合のオーバーロード/特殊化の順序は現在の標準で定義されていますか?
解決
2番目の void foo(...
は、 foo_fun :: fun
の定義では表示されないオーバーロード(特殊化ではない)であるため、 tはテンプレート定義のコンテキストで見つかります T *
は依存型であるため、式 foo((T *)0、 0)
はテンプレートのインスタンス化時間まで遅延され、インスタンス化のコンテキストも考慮されますが、標準の14.6.4.2では、関数名が unqualified-id template-id ではなく、非ADLルックアップの場合、テンプレートの定義の時点で表示される関数のみが考慮されます。 Foo
名前空間からの関数引数はないため、引数依存のルックアップは発生しないため、非テンプレートオーバーロードではなく、 foo
のテンプレートバージョンが呼び出されます。
この回答の修正について、litbに感謝します。
以下のように特殊化した場合、テンプレートのインスタンス化時に特殊化が選択されるので、関数テンプレートがに対して最初にインスタンス化された時点で関連する特殊化が表示される限り、特殊化を呼び出すことができますint
。
namespace Foo {
template<>
void foo<int>(int *, int) { puts("int"); }
}
現在の標準の第14章ですが、読みにくいです:)
編集:標準の最も関連性の高い部分を選択する必要がある場合、おそらく14.6 [temp.res] para 9になります。(わずかに省略されます)名前が template-parameterに依存しない場合、その名前の宣言は、テンプレート定義で名前が現れる場所でスコープ内になければなりません。名前はその時点で見つかった宣言にバインドされ、このバインディングはインスタンス化の時点で表示される宣言の影響を受けません。
編集、編集:ただし、16.4.4.2 [temp.dep.candidate]も考慮する必要があります。すべての相互依存関係があるため、標準を試して参照することは非常に難しく、危険です。この答えはその典型です。
他のヒント
一般的なルールとして、コンパイラの2つのバージョンのうち、後者の方がより標準的である可能性が高くなります。