20

v.fooテンプレート型 ( ) の変数から非テンプレート属性 ( ) にアクセスするT& v場合、同じ名前のテンプレート関数 ( ) がある場合、C++ はだまされてメンバー テンプレートであると認識される可能性があることがわかりましたtemplate class <T> void foo()。これは C++ 仕様からどのように説明できますか? 次の簡単なプログラムを考えてみましょう:

#include <cassert>

/** Determine whether the 'foo' attribute of an object is negative. */
template <class T>
bool foo_negative(T& v)
{
    return v.foo < 0;
}

struct X
{
    int foo;
};

int main()
{
    X x;
    x.foo = 5;
    assert(!foo_negative(x));
    return 0;
}

foo_negative任意のタイプのオブジェクトを受け取り、その foo 属性が負かどうかを判断するテンプレート関数があります。main関数は [T = X] でインスタンス化されfoo_negativeます。このプログラムは、出力なしでコンパイルおよび実行されます。

次に、この関数をプログラムの先頭に追加します。

template <class T>
void foo()
{
}

G++ 4.6.3 でコンパイルすると、次のコンパイラ エラーが発生します。

funcs.cpp: In function ‘bool foo_negative(T&)’:
funcs.cpp:13:14: error: parse error in template argument list
funcs.cpp: In function ‘bool foo_negative(T&) [with T = X]’:
funcs.cpp:25:5:   instantiated from here
funcs.cpp:13:14: error: ‘foo’ is not a member template function

(13return v.foo < 0行目と 25 行目はassert(!foo_negative(x))です。)

Clang も同様のエラーを生成します。

ワット?呼び出されることのない無関係な関数を追加して、有効なプログラムに構文エラーをどのように導入したのでしょうか? を解析するときfoo_negative、コンパイラは の型を認識していません。vまた、重要なことに、 がv.fooメンバー テンプレートであるか通常のメンバーであるかも認識していません。どうやら、解析時 (テンプレートがインスタンス化される前) に、それをメンバー テンプレートとして扱うか通常のメンバーとして扱うかを決定する必要があります。

v.fooがメンバー テンプレートであると判断した場合、はテンプレート引数< 0として渡さ0れていると見なされ、 が欠落>しているため、構文エラーになります。次に、foo_negative[T = X] でインスタンス化するとX::foo、メンバー テンプレートではないため、別のエラーが発生します。

しかし、なぜv.fooメンバー テンプレートであると考えるのでしょうか。このあいまいさはまさにtemplateキーワードの目的です: と書いた場合v.template foo、C++ にメンバー テンプレートを期待するように明示的に指示することになりますが、templateキーワードを使用しませんでした! メンバー テンプレートを参照していないので、通常のメンバーであると想定する必要があります。メンバーと同じ名前の関数があるという事実は、何の効果もないはずです。なぜですか?GCC と clang は一貫しているため、コンパイラのバグではありません。

4

3 に答える 3

6

コンパイラのバグのようです。

標準は次のように述べています。

名前ルックアップ (3.4) の後、名前がテンプレート名であるか、operator-function-id またはliteral-operator-id がオーバーロードされた関数のセットを参照しており、そのメンバーのいずれかが関数テンプレートである場合、これが続く場合< の場合、< は常にテンプレート引数リストの区切り文字として使用され、小なり演算子として使用されることはありません。

および 3.4.5/1 で

クラス メンバ アクセス式 (5.2.5) で、. または -> トークンの直後に識別子が続き、その後に < が続く場合、その識別子を調べて、< がテンプレート引数リスト (14.2) の先頭であるか、小なり演算子であるかを判断する必要があります。識別子は、最初にオブジェクト式のクラスで検索されます。識別子が見つからない場合は、postfix-expression 全体のコンテキストで検索され、クラス テンプレートに名前が付けられます。

標準は、名前の検索が非メンバー関数テンプレートをここで見つけることができることを示していないようです。いずれにせよ、 の意味は<、インスタンス化時ではなく、テンプレート定義時に決定する必要があります (それでは遅すぎます)。

于 2012-05-20T20:00:55.800 に答える
5

これはバグです。

コードは MSVC (2011) で正常に動作しています。コンパイラのパーサーが「<」をテンプレート ステートメントの開始トークンとして変換したと思います。しかし、なぜ Clang と GCC に同時にこのバグがあるのでしょうか?

ここここでバグを報告してください。

これも興味深いかもしれません: g++/Clang の別のバグ? 【C++テンプレートは楽しい】

于 2012-05-20T10:52:38.480 に答える
3

Clang では、これはPR11856で、約 2.5 か月前に修正されました。Clang トランクと Clang 3.1 は、このコードでエラーを報告しません。Clang バグには、このコードが拒否された理由と、コードが正しい理由の説明が含まれています。

ここで重要な段落は[basic.lookup.classref]p1です。

「クラス メンバー アクセス式 (5.2.5) で、.or->トークンの直後に識別子が続き、その後に が続く場合、識別子を検索して、 がテンプレート引数リスト (14.2) の先頭<かどうかを判断する必要があります。<より小さい演算子. 識別子は、最初にオブジェクト式のクラスで検索されます. 識別子が見つからない場合は、接尾辞式全体のコンテキストで検索され、クラス テンプレートを指定する必要があります."

依存しているためv、おそらく識別子が見つからないため、 postfix-expression全体のコンテキストで見るとどうなるかを検討します 。関数テンプレートを見つけたので、 template-idの始まりがあると結論付けるべきではありません。

于 2012-05-29T20:44:23.980 に答える