21

GCC は次のコードを受け入れます。

template <typename T>
struct meta
{
    typedef typename T::type type;
};

struct S {};

template <typename T>
typename meta<T>::type foo(T, S);

int foo(int, int);      

int main()
{
    foo(0, 0);
}

しかし、clang は次のエラーで拒否します。

test.cpp:4:22: error: type 'int' cannot be used prior to '::' because it has no members
    typedef typename T::type type;
                     ^
test.cpp:10:10: note: in instantiation of template class 'meta<int>' requested here
typename meta<T>::type foo(T, S);
         ^
test.cpp:10:24: note: while substituting deduced template arguments into function template 'foo' [with T = int]
typename meta<T>::type foo(T, S);
                       ^

これは、過負荷の解決中に GCC と clang が特定の操作を実行する順序の違いを示唆しているようです。テンプレート候補の戻り値の型をインスタンス化しようとする前にS、GCC は 2 番目のパラメーター ( vs. int)の型の不一致のためにテンプレート候補を破棄するようですが、clang はその逆を行うようです。

誰が正しいですか?

この質問は、テンプレート ライブラリの作成者にとって重要な意味を持つと思います。具体的には、clang が正しい場合、テンプレートの作成者はfoo、エラーを置換の失敗に変えるために追加の作業を行う必要があります。

EDIT : 次のやや単純な例は、GCC と clang の両方で拒否され、同様のエラーが発生することに注意してください。

template <typename T>
struct meta
{
    typedef typename T::type type;
};

template <typename T>
typename meta<T>::type foo(T);

int foo(int);      

int main()
{
    foo(0);
}

GCCは、「関数型とそのテンプレートパラメータ型の直接のコンテキストで無効な型と式のみが推論の失敗を引き起こす可能性がある」ことを知っていることを示唆しています. この例と元の例の違いは、元の例に 2 番目の関数パラメーターが存在することです。これに基づいて、GCC は戻り値の型で置換を実行しようとする前にテンプレート候補を破棄します。問題は、GCCがその順序で物事を行うのが正しいかどうか、または引数の型の一致を考慮する前に戻り値の型で置換を実行しようとする必要があるかどうかだと思います。

更新: Luc Danton の回答により、clang がコードを拒否するのは正しいと確信しました。それに応じて、GCC バグを提出しました

4

2 に答える 2

11

ここでは、Clang と g++ の両方が正しいです。

Luc Danton's answer によると、コンパイラは関数テンプレートを推測することが許可されT = intています。foo次に、その値を の宣言に代入するときにfoo、 の暗黙的なインスタンス化meta<int>が必要になり、代入の直接のコンテキスト外でエラーが発生します (したがって、SFINAE は適用されません)。したがって、Clang がこのコードを拒否するのは正しいことです。

ただし、[temp.inst]p7は次のように述べています。

オーバーロード解決プロセスが、クラス テンプレート定義をインスタンス化せずに呼び出す正しい関数を決定できる場合、そのインスタンス化が実際に行われるかどうかは不明です。

非テンプレートfooは呼び出しの引数と完全に一致するため、コンパイラは、関数テンプレートの特殊化が実行可能な最適な関数になることはなく、引数の推定と置換を実行する必要がないと判断する可能性があります。したがって、g++ はこのコードを拒否しないのが正しいです。

于 2013-07-07T06:00:16.267 に答える
5

C++03 では、通常 SFINAE と呼ばれるものの仕様の一部として、この表現を使用します (14.8.2 テンプレート引数推定 [temp.deduct]、パラグラフ 2)。

[...] テンプレート パラメーターまたは関数テンプレートの関数型での置換が無効な型になる場合、型推定は失敗します。[...]

対照的に、C++11 はこの文言を使用します (14.8.2 テンプレート引数推定 [temp.deduct]、パラグラフ 8):

置換の結果が無効な型または式になる場合、型推定は失敗します。[...]関数型とそのテンプレート パラメーター型の直接のコンテキストで無効な型と式のみが、推論の失敗を引き起こす可能性があります。[...]

強調は私です。私が理解しているように、C++ 11 では文言が改善され、SFINAE になるべきもの (いわゆるソフト エラー) とそうでないもの (ハード エラー) を明確に概説するようになりました。この 2008 年の論文は、当時進行していた議論が現在の規則につながった例です。

これを念頭に置いて、C++03 によると、実装がコードを受け入れるのに適切である可能性があります (そして、おそらくそうすべきです)。ただし、C++11 の実装では拒否する必要があると思われます。エラー ( int::type) はmeta<int>ではなく のコンテキストにありfoo<int>ます。

于 2012-08-18T05:17:10.727 に答える