【问题标题】:Confusing Template error令人困惑的模板错误
【发布时间】:2011-04-16 17:27:24
【问题描述】:

我一直在玩 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 关键字以使代码在语法上正确?

【问题讨论】:

  • 您是否尝试将其插入箭头指向的位置?
  • 类似于thisthis

标签: c++ templates llvm clang


【解决方案1】:

ISO C++03 14.2/4:

当成员模板特化的名称出现在 .或 -> 在后缀表达式中,或在限定 ID 中的嵌套名称说明符之后,并且后缀表达式或限定 ID 显式依赖于模板参数 (14.6.2),成员模板名称必须以关键字模板为前缀。否则,该名称被假定为命名一个非模板。

t-&gt;f0&lt;U&gt;();f0&lt;U&gt;是出现在-&gt;之后的成员模板特化,它显式依赖于模板参数U,因此成员模板特化必须以template关键字为前缀。

所以将t-&gt;f0&lt;U&gt;() 更改为t-&gt;template f0&lt;U&gt;()

【讨论】:

  • 有趣的是,我认为将表达式放在括号中:t-&gt;(f0&lt;U&gt;()) 会解决这个问题,因为我认为这会将 f0&lt;U&gt;() 放入独立表达式中......好吧,我想错了,看来。 ..
  • 您能否评论一下为什么会这样?为什么 C++ 需要这种语法?
  • 是的,这很奇怪。该语言可以“检测”模板关键字需要存在。如果它可以做到这一点,那么它应该只是在其中“插入”关键字。
【解决方案2】:

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

#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&lt;int()&gt; 之前省略template 时会打印0,但在插入时会打印1。我把它作为一个练习来弄清楚代码的作用。

【讨论】:

  • 现在这是一个可怕的例子!
  • 我无法重现您在 Visual Studio 2013 中描述的行为。它始终调用 f&lt;U&gt; 并始终打印 1,这对我来说非常有意义。我仍然不明白为什么需要 template 关键字以及它有什么区别。
  • @Violet VSC++ 编译器不是兼容的 C++ 编译器。如果您想知道为什么 VSC++ 总是打印 1,则需要一个新问题。
  • 这个答案解释了为什么需要templatestackoverflow.com/questions/610245/…,而不是仅仅依赖于难以理解的标准术语。如果该答案中的任何内容仍然令人困惑,请报告。
  • @JohannesSchaub-litb:谢谢,一个很好的答案。原来,我以前读过它,因为它已经被我点赞了。显然,我的记忆力很好。
【解决方案3】:

将它插入到插入符号所在的点之前:

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

编辑:如果你像编译器一样思考,这条规则的原因会变得更清楚。 编译器一般一次只向前看一两个标记,一般不会“向前看”到表达式的其余部分。[编辑:见评论]关键字的原因与为什么需要 typename 关键字来指示依赖类型名称:它告诉编译器“嘿,您将要看到的标识符是模板的名称,而不是静态数据成员的名称,后跟一个 less-比签名”。

【讨论】:

  • 我本来从来没有能够猜到...但是谢谢你;-)。显然,C++ 总有一些东西要学!
  • 即使无限前瞻,您仍然需要template。在某些情况下,无论有没有template,都会产生具有不同行为的有效程序。所以这不仅仅是一个语法问题(t-&gt;f0&lt;int()&gt;(0) 在语法上对于小于和模板参数列表版本都是有效的)。
  • @Johannes Schaub - litb:是的,所以与向前看相比,为表达分配一致的语义意义更多的是问题。
【解决方案4】:

摘自C++ Templates

.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。如果没有额外使用模板,编译器不知道后面的小于标记 (

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

【讨论】:

    猜你喜欢
    • 2018-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-30
    相关资源
    最近更新 更多