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


91

私はしばらく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、構文的に正しいコードにするために、キーワードをどこに挿入するのかを正確に理解するのに苦労していますか?


11
矢印が指しているところに挿入してみましたか?
マイクシーモア

回答:


104

ISO C ++ 03 14.2 / 4:

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

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

したがって、に変更t->f0<U>()t->template f0<U>()ます。


興味深いことに、式を括弧でt->(f0<U>())f0<U>()

26
なぜそうなのかコメントしていただけませんか?なぜC ++はこの種の構文を必要とするのでしょうか?
好奇心が

2
ええ、これは変です。言語は、テンプレートキーワードが存在する必要があることを「検出」できます。それができるなら、それ自体にキーワードを「挿入」する必要があります。
EnricoBorba20年

26

他の人が指摘したことに加えて、コンパイラが決心できず、両方の解釈がインスタンス化時に代替の有効なプログラムを生成する可能性があることに注意してください

#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;
}

これは0template前を省略しf<int()>1とき、挿入したときに印刷されます。コードが何をするのかを理解するための演習として残しておきます。


3
これは悪魔のような例です!
Matthieu M.

1
Visual Studio 2013で説明している動作を再現できません。常に呼び出しf<U>、常に印刷1するので、私には完全に理にかなっています。templateキーワードが必要な理由とそれがどのような違いをもたらすのか、私はまだ理解していません。
バイオレットキリン2014

@Violet VSC ++コンパイラは準拠したC ++コンパイラではありません。あなたはVSCが++常に1印刷し理由を知りたい場合は、新しい質問が必要とされている
litb -ヨハネス・シャウブ

1
この答えは、なぜtemplate必要なのかを説明しています:stackoverflow.com/questions/610245/…理解するのが難しい標準的な用語だけに頼ることなく。その回答のいずれかがまだ混乱している場合は報告してください。
Johannes Schaub-litb 2014

@ JohannesSchaub-litb:ありがとう、素晴らしい答え。結局、私はそれを以前に読んだことがあります。どうやら、私の記憶はまあです。
バイオレットキリン2014

12

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

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

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


1
私はそれを推測することができなかっただろう...しかしありがとう;-)。C ++について学ぶべきことが常にあることは明らかです!

3
無限の先読みがある場合でも、必要になりますtemplate。ある場合とない場合の両方で、template動作が異なる有効なプログラムが生成される場合があります。したがって、これは構文上の問題だけではありません(t->f0<int()>(0)より小さいバージョンとテンプレート引数リストバージョンの両方で構文的に有効です)。
Johannes Schaub-litb 2010

@Johannes Schaub-litb:そうですね。ですから、先を見据えるよりも、式に一貫した意味を割り当てることの方が問題です。
ダグ

11

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などの同様の表記)は、テンプレート内でのみ、テンプレートパラメーターに依存するものに従う場合にのみ使用する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.