3

最近,我在读这本书:C++ templates: the complete guideDavid Vandevoorde 和 Nicolai M. Josuttis 写的。

特别是关于模板解析引用的书 pp 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*a一样C<T>* a?我尝试用 替换C*aC<T>* a没有报错,那么是?C* a的简写C<T>* a

非常感谢!

4

3 回答 3

2

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

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

像普通(非模板)类一样,类模板有一个注入类名(第 9 条)。注入的类名可以用作模板名类型名当它template-argument-list一起使用时,作为模板模板参数的模板参数,或作为朋友类模板声明的详细类型说明符中的最终标识符,它指的是类模板本身. 否则,它等价于template-name后跟包含在 中的类模板的模板参数<>

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

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

与普通(非模板)类一样,类模板有一个注入类名(第 9 条)。注入的类名可以与模板参数列表一起使用,也可以不与模板参数列表一起使用。 当它在没有模板参数列表的情况下使用时,它等效于注入的类名,后跟包含在 中的类模板的模板参数<> 当它与template-argument-list一起使用时,它指的是指定的类模板特化,可以是当前特化或另一个特化。

除了反转逻辑,我们看到 C++03 包含一个标识符引用模板的情况(当提供模板参数时),C++11 增加了两个额外的情况,其中一个影响了这段代码。

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

底线:要学习语言,尤其是模板,您需要一本关于 C++11 的书。旧书在架构和高级设计方面仍然有用,但无法解释出版后发生变化的语言复杂性。

于 2013-04-21T05:43:48.170 回答
1

试图回答你的问题:

  1. 跨供应商和版本的不同编译器会生成不同的诊断信息。您不能依赖从一个到另一个的确切消息。

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

类名被插入到在看到类名之后立即声明它的作用域中。类名也被插入到类本身的范围内;这被称为注入类名。出于访问检查的目的,注入的类名被视为公共成员名。类说明符通常称为类定义。一个类在看到其类说明符的右大括号后被认为已定义,即使它的成员函数通常尚未定义。可选的属性说明符序列属于类;此后,无论何时命名,attribute-specifier-seq 中的属性都被视为该类的属性。

  1. 原因与上下文C* a相同C<T>* a:两者都发生在C. C++ 允许一个“捷径”,因为模板类可以引用它自己的实例化,而无需在可以假设参数的上下文中使用模板参数(这是其中之一)。如果您想引用C具有不同模板参数的实例化,则需要明确说明。

您收到特定错误的原因是C<void>您尝试C使用不同的模板参数实例化 a 时类型不完整。

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

于 2013-04-21T04:21:10.040 回答
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* a的简写C<T>* a吗?

仅在班级自己的范围内。(包括成员定义范围)例如

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

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

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

于 2013-04-21T04:11:20.113 回答