web-dev-qa-db-ja.com

紛らわしいテンプレートエラー

私はしばらくclangで遊んでいましたが、テンプレートエラーから回復するためのヒントを提供することになっている「test/SemaTemplate/dependent-template-recover.cpp」(clangディストリビューション内)につまずきました。

全体を最小限の例に簡単にまとめることができます。

template<typename T, typename U, int N> struct X {
    void f(T* t)
    {
        // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}}
        t->f0<U>();
    }
};

Clangによって生成されるエラーメッセージ:

tpl.cpp:6:13: error: use 'template' keyword to treat 'f0' as a dependent template name
         t->f0<U>();
            ^
            template 
1 error generated.

...しかし、コードを構文的に正しくするためにtemplateキーワードを挿入する必要がある場所を正確に理解するのは困難ですか?

78
user350814

ISO C++ 03 14.2/4:

メンバーテンプレート特化の名前がの後に表示される場合。または-> postfix-expression内、またはqualified-id内のnested-name-specifierの後、postfix-expressionまたはqualified-idは明示的にtemplate-parameter(14.6.2)に依存しますメンバーテンプレート名には、キーワードtemplateをプレフィックスとして付ける必要があります。それ以外の場合、名前は非テンプレートの名前と見なされます。

t->f0<U>();では、_f0<U>_は、_->_の後に表示されるメンバーテンプレートの特殊化であり、テンプレートパラメーターUに明示的に依存するため、メンバーテンプレートの特殊化の前にtemplateキーワードを付ける必要があります。

t->f0<U>()t->template f0<U>()に変更します。

83
Prasoon Saurav

他の人が指摘した点に加えて、コンパイラーが思いつかないことがあり、インスタンス化時に両方の解釈が代替の有効なプログラムを生成する場合があることに注意してください

_#include <iostream>

template<typename T>
struct A {
  typedef int R();

  template<typename U>
  static U *f(int) { 
    return 0; 
  }

  static int f() { 
    return 0;
  }
};

template<typename T>
bool g() {
  A<T> a;
  return !(typename A<T>::R*)a.f<int()>(0);
}


int main() {
  std::cout << g<void>() << std::endl;
}
_

これは、f<int()>の前のtemplateを省略すると_0_を出力しますが、挿入するときは_1_を出力します。コードの機能を理解するための演習として残しておきます。

キャレットがあるポイントの直前に挿入します。

template<typename T, typename U, int N> struct X {
     void f(T* t)
     {
        t->template f0<U>();
     }
};

編集:コンパイラーのように考えると、このルールの理由がより明確になります。 コンパイラは通常、一度に1つまたは2つのトークンのみを先読みし、通常は残りの式を「先読み」しません。[編集:コメントを参照]キーワードの理由は、依存型の名前を示すためにtypenameキーワードが必要な理由と同じです。静的データメンバーの名前の後に小なり記号が続くのではなく、テンプレートの」。

10
Doug

C++テンプレート からの抜粋

.templateコンストラクトtypenameの導入後に、非常によく似た問題が発見されました。標準のビットセットタイプを使用した次の例を考えてみましょう。

template<int N> 
void printBitset (std::bitset<N> const& bs) 
{ 
    std::cout << bs.template to_string<char,char_traits<char>, 
                                       allocator<char> >(); 
} 

この例の奇妙な構造は.templateです。テンプレートをそのように余分に使用しないと、コンパイラは、後に続く小なりトークン(<)が実際には「小なり」ではなく、テンプレート引数リストの始まりであることを知りません。これは、ピリオドの前の構造がテンプレートパラメータに依存する場合にのみ問題になることに注意してください。この例では、パラメーターbsはテンプレートパラメーターNに依存します。

結論として、.template表記(および-> templateなどの同様の表記)はテンプレート内でのみ使用し、テンプレートパラメータに依存する何かに従う場合にのみ使用する必要があります。

8
Chubsdad