最近,我正在阅读由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
因此,我有以下问题:
b
没问题,但是gcc给出了错误;同时,没有检测到书中列出的其他错误?为什么,这是一个可能的编译器错误或书中的错误?injected class names
是什么意思?如何识别名称injected class names
和哪些名称不是?C*a
与C<T>* a
相同?我尝试将C*a
替换为C<T>* a
,未报告错误,C* a
的简写是C<T>* a
吗?非常感谢!
答案 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)
试着回答你的问题:
供应商和版本之间的不同编译器会生成不同的诊断信息。您不能依赖彼此之间的确切消息。
“注入的类名”在§9第2段的标准中定义:
将类名插入到声明它的作用域中 看到类名后立即。类名也是 插入到类本身的范围内;这被称为 注入的类名。出于访问检查的目的, inject-class-name被视为公共成员名称。一个 class-specifier通常称为类定义。一类 被认为是在其类说明符的右括号之后定义的 虽然它的成员功能一般尚未出现,但已被看到 定义。可选的attribute-specifier-seq属于该类; 之后属性说明符-seq中的属性 在命名时考虑类的属性。
C* a
与C<T>* a
相同的原因是由于上下文:两者都出现在C
的声明中。 C ++允许一个“快捷方式”,因为模板化的类可以引用它自己的实例化,而不需要在可以假定参数的上下文中的模板参数(这是其中之一)。如果你想引用具有不同模板参数的C
实例化,你需要明确它。您收到特定错误的原因是{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* a
是C<T>* a
的缩写吗?
仅限于班级自己的范围。 (包括成员定义范围),例如
template <typename T>
void C<T>::f() {
C a; // Same as C<T> a;
}
为什么编译器会生成完全不同的错误消息?这本书 说b没关系,但是gcc给出了错误;同时,列出了其他错误 在书中没有检测到?为什么,这是一个可能的编译器错误或 书中的错误?
除了上面解释的事情之外,原因可能是这本书相对陈旧,编译器从那时起就发生了变化。您不能指出错误消息完全相同。