令人困惑的模板错误

78 c++ templates llvm clang

我一直玩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>();
    }
};
Run Code Online (Sandbox Code Playgroud)

clang产生的错误消息:

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

...但我很难理解究竟应该插入template关键字以使代码在语法上正确?

Pra*_*rav 85

ISO C++ 03 14.2/4:

当成员模板专业化的名称出现之后.或 - >在postfix-expression中,或在qualified-id中的nested-name-specifier之后,postfix-expression或qualified-id显式依赖于template-parameter(14.6.2),成员模板名称必须是以关键字模板为前缀.否则,假定该名称命名非模板.

In t->f0<U>(); f0<U>是一个成员模板特化,它出现在后面->并明确依赖于模板参数U,因此成员模板特化必须以template关键字为前缀.

所以t->f0<U>()改为t->template f0<U>().

  • 您可以评论为什么会这样吗?为什么C++需要这种语法? (14认同)
  • 是的,这很奇怪。该语言可以“检测”模板关键字是否需要存在。如果它可以做到这一点,那么它应该只是在其中“插入”关键字。 (3认同)

Joh*_*itb 24

除了其他人提出的观点之外,请注意有时编译器无法下定决心,并且两种解释都可以在实例化时产生其他有效的程序.

#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;
}
Run Code Online (Sandbox Code Playgroud)

0在省略template之前f<int()>1在插入时打印.我把它留作练习来弄清代码的作用.

  • 现在,这是一个恶魔般的例子! (3认同)
  • 这个答案解释了为什么需要“template”:/sf/ask/42717181/,而无需单独依赖使用难以理解的标准术语。如果该答案中的任何内容仍然令人困惑,请报告。 (3认同)

Dou*_*oug 10

在插入符号的位置之前插入它:

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

编辑:如果您像编译器一样思考,此规则的原因会变得更清晰.编译器通常只能一次向前看一个或两个令牌,并且通常不会"向前看"表达式的其余部分.[编辑:请参阅注释]关键字的原因与您需要typename关键字指示依赖类型名称的原因相同:它告诉编译器"嘿,您将看到的标识符是模板的名称,而不是静态数据成员的名称后跟一个小于号的符号".

  • 即使有无限的前瞻,你仍然需要`模板`.有些情况下,有和没有`template`都会产生具有不同行为的有效程序.所以这不仅仅是一个语法问题(`t-> f0 <int()>(0)`在语法上对于less-than和template参数列表版本都是有效的. (3认同)

Chu*_*dad 8

摘自C++模板

.template构造在引入typename之后发现了一个非常类似的问题.请考虑使用标准bitset类型的以下示例:

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

这个例子中的奇怪构造是.template.如果没有额外使用模板,编译器就不会知道后面的小于令牌(<)实际上并不是"小于",而是模板参数列表的开头.请注意,仅当句点之前的构造取决于模板参数时,才会出现此问题.在我们的示例中,参数bs取决于模板参数N.

总之,.template表示法(以及类似符号,如 - >模板)应仅在模板内使用,并且只有在它们遵循依赖于模板参数的内容时才使用.