gcc编译器没有显示类模板注入名称的正确错误

时间:2013-04-21 03:48:43

标签: c++ templates name-lookup

最近,我正在阅读由David Vandevoorde和Nicolai M. Josuttis撰写的书C++ templates: the complete guide

特别是关于模板解析引用书第126页。

  

类模板也注入了类名,但是,它们比普通的注入类名更奇怪:它们后面跟着模板参数(在这种情况下它们是注入类模板名),但是如果它们后面没有模板参数它们以参数作为参数来表示类(或者,对于部分特化,它的特化参数)。

相关代码除外如下:

template<template<typename> class TT> 
class X
{
};

template <typename T>
class C
{
  C* a; //OK, same as "C<T>* a"
  C<void> b; // OK
  X<C> c;  //Error, C without a template argument list does not denote a template
  X< ::C>d; 
};

int main()
{  
   return 0;
}

上面的代码示例试图解释整个引用的段落。

我在gcc 4.5.3中编译了上面的代码,输出:

error: field ‘b’ has incomplete type

因此,我有以下问题:

  1. 为什么编译器会生成完全不同的错误消息?书中说b没问题,但是gcc给出了错误;同时,没有检测到书中列出的其他错误?为什么,这是一个可能的编译器错误或书中的错误?
  2. injected class names是什么意思?如何识别名称injected class names和哪些名称不是?
  3. 为什么C*aC<T>* a相同?我尝试将C*a替换为C<T>* a,未报告错误,C* a的简写是C<T>* a吗?
  4. 非常感谢!

3 个答案:

答案 0 :(得分:2)

注意: inject-class-name 只是用于声明类的标识符(与其他也引用同一类的名称相对,例如typedef名称)。

以下是C ++ 11标准的相关引用,第14.6.1p1节:

  

与普通(非模板)类一样,类模板具有注入类名(第9节)。 inject-class-name可以用作模板名称类型名称使用时使用 template-argument-list 作为模板的模板参数 template-parameter ,或作为朋友类模板声明的 elaborated-type-specifier 中的最终标识符,它指的是类模板本身。否则,它等同于 template-name ,后跟<>中包含的类模板的 template-parameters

很明显,这在C ++ 11中是合法的。

但是,本书正确描述了C ++ 03的行为:

  

与普通(非模板)类一样,类模板具有注入类名(第9节)。 inject-name-name可以使用或不使用 template-argument-list 在没有 template-argument-list 的情况下使用它时,它等同于inject-name-name,后跟包含的类模板的 template-parameters <>中。当它与 template-argument-list 一起使用时,它引用指定的类模板特化,可以是当前的特化或其他特化。 / p>

除了颠倒逻辑之外,我们看到C ++ 03包含一个案例,其中标识符引用模板(当提供模板参数时),而C ++ 11增加了两个附加案例,其中一个案例影响了这个代码。

所以在C ++ 03中,代码等同于X<C<T>> c;,这是一个错误,因为X需要传递模板而不是类型。

结论:要了解语言,特别是模板,您需要a book on C++11。较旧的书籍在建筑和高级设计方面仍然有用,但无法解释在出版后发生变化的语言错综复杂。

答案 1 :(得分:1)

试着回答你的问题:

  1. 供应商和版本之间的不同编译器会生成不同的诊断信息。您不能依赖彼此之间的确切消息。

  2. “注入的类名”在§9第2段的标准中定义:

  3.   

    将类名插入到声明它的作用域中   看到类名后立即。类名也是   插入到类本身的范围内;这被称为   注入的类名。出于访问检查的目的,   inject-class-name被视为公共成员名称。一个   class-specifier通常称为类定义。一类   被认为是在其类说明符的右括号之后定义的   虽然它的成员功能一般尚未出现,但已被看到   定义。可选的attribute-specifier-seq属于该类;   之后属性说明符-seq中的属性   在命名时考虑类的属性。

    1. C* aC<T>* a相同的原因是由于上下文:两者都出现在C的声明中。 C ++允许一个“快捷方式”,因为模板化的类可以引用它自己的实例化,而不需要在可以假定参数的上下文中的模板参数(这是其中之一)。如果你想引用具有不同模板参数的C实例化,你需要明确它。
    2. 您收到特定错误的原因是{I} C<void>在您尝试使用不同的模板参数实例化C时是不完整的类型。

      我觉得奇怪的是,查看代码,我认为它应该干净地编译,因为你实际上从未实例化任何一个模板,因此C的类型完整性无关紧要。

答案 2 :(得分:0)

我认为这本书忘了*那里

C<void> *b;

否则即使没有模板,声明也无效。因为当编译器到达该行时C尚未完全声明。它与尝试使用前向声明类相同。

例如,这是无效的。

class A {
    A a;
    ^// A has incomplete type here
};

然而这是有效的。

class A {
    A *a;
};

注入类名称是指类的模板参数在类的自己的范围中注入时的用法。

tempalte<typename T>
class C {
    C* a;  // same as C<T>* a;
};

我认为这本书完全解释了它。


  

C* aC<T>* a的缩写吗?

仅限于班级自己的范围。 (包括成员定义范围),例如

template <typename T>
void C<T>::f() {
    C a;            // Same as C<T> a;
}

  

为什么编译器会生成完全不同的错误消息?这本书   说b没关系,但是gcc给出了错误;同时,列出了其他错误   在书中没有检测到?为什么,这是一个可能的编译器错误或   书中的错误?

除了上面解释的事情之外,原因可能是这本书相对陈旧,编译器从那时起就发生了变化。您不能指出错误消息完全相同。