9

私が手に入れることができたすべてのコンパイラは、これで問題ないと同意しています。

template <typename Check, typename... T>
auto foo(Check, T...) -> void;

template <typename... T>
auto foo(int, T...) -> void;

int main()
{
  foo(7, "");
}

ただし、次のコード (関数パラメーターから推測できない先頭のテンプレート パラメーターを使用) は、gcc によるとあいまいです。

template <typename X, typename Check, typename... T>
auto bar(Check, T...) -> void;

template <typename X, typename... T>
auto bar(int, T...) -> void;

int main()
{
  bar<void>(7, ""); // ambiguous according to gcc
  bar<void>(7);     // just fine
}

一方、clang、msvc、icc はこれに非常に満足しています。

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

標準の各セクションへの参照が推奨されます。

4

2 に答える 2

4

これはコアイシュー 200です。

テンプレート関数の部分的な順序付けが 14.5.6.2 [temp.func.order] パラグラフ 3 ~ 5 でどのように決定されるかについての説明では、推定されていないテンプレート パラメータについては何も規定されていません。たとえば、次のコードの関数呼び出しはあいまいですが、1 つのテンプレートが他のテンプレートよりも「明らかに」特殊化されています。

template <class T> T f(int);
template <class T, class U> T f(U);
void g() {
    f<int>(1);
}

その理由は、どちらの関数パラメーター リストもテンプレート パラメーターTを推測できないためです。両方の推論が失敗するため、どちらのテンプレートも他方よりも特殊化されているとは見なされず、関数呼び出しはあいまいです。

この問題が縮小されたコア問題 214の解決により、 [temp.deduct.partial]/11が導入されました。

ほとんどの場合、推論が成功するためにはすべてのテンプレート パラメータに値が必要ですが、部分的な順序付けの目的で、テンプレート パラメータは、部分的な順序付けに使用されている型で使用されていない限り、値を持たないままにすることができます

明らかに、GCC のこの文言の実装は、パックが機能するようになるとバグがあります。

于 2016-09-02T15:24:25.813 に答える
0

IMHO GCC は間違っていて、CLANG は正しいと思います。以下で私の主張を正当化しようとします。

標準§14.8.3/p1によると、過負荷の解決 [temp.over] ( Emphasis Mine ) :

関数テンプレートは、その名前の (非テンプレート) 関数または同じ名前の (他の) 関数テンプレートのいずれかによってオーバーロードできます。その名前への呼び出しが (明示的に、または演算子表記を使用して暗黙的に) 書き込まれると、テンプレート引数推定 (14.8.2) と明示的なテンプレート引数のチェック (14.3) が関数テンプレートごとに実行され、その関数テンプレートで使用できるテンプレート引数値 (存在する場合) を見つけて、関数テンプレートの特殊化をインスタンス化できます。呼び出し引数で呼び出されます。関数テンプレートごとに、引数の推定とチェックが成功した場合、テンプレート引数 (推定および/または明示的) を使用して、オーバーロード解決で使用される候補関数セットに追加される単一の関数テンプレートの特殊化の宣言を合成します。 .特定の関数テンプレートについて、引数推定が失敗するか、合成された関数テンプレートの特殊化が不適切な形式である場合、そのような関数はそのテンプレートの候補関数のセットに追加されません。候補関数の完全なセットには、すべての合成された宣言と、同じ名前の非テンプレート オーバーロード関数がすべて含まれています。合成された宣言は、13.3.3 で明示的に説明されている場合を除き、オーバーロード解決の残りの部分で他の関数と同様に扱われます。144

[例:

template<class T> T max(T a, T b) { return a>b?a:b; }
void f(int a, int b, char c, char d) {
int m1 = max(a,b); // max(int a, int b)
char m2 = max(c,d); // max(char a, char b)
int m3 = max(a,c); // error: cannot generate max(int,char)
}

144) 関数テンプレートの特殊化のパラメーターには、テンプレート パラメーターの型が含まれていません。推定された引数で許可される変換のセットは制限されます。これは、引数推定プロセスが呼び出し引数と完全に一致するか、許可された制限された変換によってブリッジできる方法のみが異なるパラメーターを持つ関数テンプレートを生成するためです。推定されていない引数は、変換の全範囲を許可します。13.3.3 では、テンプレートの特殊化よりも非テンプレート関数が優先されることを指定していることにも注意してください。

上記から、明示的なテンプレート引数がチェックされ、チェックが成功した場合は、オーバーロード解決のために候補関数に追加される特殊化を合成するために使用されることがわかります。したがって、明示的に指定したという事実はX、プロセスには関係ありません。

また、C++ 標準§13.3.3/p1.7 から Best viable function [over.match.best] :

F1およびF2は関数テンプレートの特殊化であり、 14.5.6.2 で説明されている半順序規則に従って、関数テンプレートF1は のテンプレートよりも特殊化されています。F2

§14.5.6.2/p3から、関数テンプレートの部分的な順序付け [temp.func.order]部分的な順序付けでは、パラメーター パックも有効になるため、ここでも問題ありません。

今:

template <typename X, typename... T>
auto bar(int, T...) -> void;

次のものよりも専門的です。

template <typename X, typename Check, typename... T>
auto bar(Check, T...) -> void;

したがって、次のように呼び出します。

bar<void>(7, "");

曖昧ではありません。

上記に基づいて、これは GCC のバグだと思います。

于 2016-09-02T08:22:53.900 に答える