39

次のスニペットを検討してください。

struct Base { };
struct Derived : Base { };

void f(Base &) { std::cout << "f(Base&)\n"; }

template <class T = int>
void g() {
    Derived d;
    f(T{} ? d : d); // 1
}

void f(Derived &) { std::cout << "f(Derived&)\n"; }

int main() {
    g();
}

この場合、fatへの関数呼び出し// 1はフェーズ 1 で検索する必要があると思います。その引数の型は明白であり、スコープ内の唯一のものにDerived&解決されるからです。f(Base&)

Clang 3.8.0 は私に同意しますが、GCC 6.1.0 は同意せず、がピックアップさfれるフェーズ 2 までのルックアップを延期します。f(Derived&)

どのコンパイラが正しいですか?

4

3 に答える 3

23

C++ 標準の最新バージョンを使用現在n4582 .

セクション 14.6 (p10) では、名前がテンプレート パラメーターに依存していない場合、名前は宣言の時点でバインドされると述べています。テンプレートパラメータに依存する場合、これはセクション 14.6.2 で定義されています。

セクション 14.6.2.2 は、部分式が型依存である場合、式は型依存であると述べています。

への呼び出しf()はそのパラメータに依存しているためです。パラメーターの型を調べて、型に依存しているかどうかを確認します。パラメータはFalse<T>::value ? d : d. ここで、最初の条件は type に依存しTます。

したがって、呼び出しは宣言ではなくインスタンス化の時点でバインドされると結論付けます。したがって、次のようにバインドする必要があります。void f(Derived &) { std::cout << "f(Derived&)\n"; }

したがって、g++ にはより正確な実装があります。

14.6 名前解決 [temp.res]

パラ 10:

名前がtemplate-parameter (14.6.2 で定義) に依存しない場合、その名前の宣言 (または宣言のセット) は、名前がテンプレート定義に現れるポイントでスコープ内にあるものとします。名前はその時点で見つかった宣言 (または複数の宣言) にバインドされ、このバインディングはインスタンス化の時点で可視である宣言の影響を受けません。

14.6.2.2 型依存式 [temp.dep.expr]

以下で説明する場合を除き、部分式が type-dependent である場合、式は type-dependentです。

于 2016-06-01T09:23:17.570 に答える
1

C++ ドラフト ( n4582 ) §14.7.1.5 によると:

関数テンプレートの特殊化が明示的にインスタンス化または明示的に特殊化されていない限り、関数テンプレートの特殊化は、関数定義の存在を必要とするコンテキストで特殊化が参照されるときに暗黙的にインスタンス化されます。呼び出しが関数テンプレートの明示的な特殊化または明示的に特殊化されたクラス テンプレートのメンバー関数に対するものでない限り、関数テンプレートまたはクラス テンプレートのメンバー関数の既定の引数は、関数が必要なコンテキストで呼び出されるときに暗黙的にインスタンス化されます。デフォルト引数の値。

これについては、gccの方が正しいと思います。

たとえば、特殊なバージョンを作成すると、両方のコンパイラが同じことを行いvoid g()ます。

于 2016-06-01T09:22:45.857 に答える