Недавно я читал книгу: 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
?Большое спасибо!
Примечание: нагнетаемого имя класса это просто идентификатор, используемый для объявления класса (в отличие от других имен, которые также ссылаются на тот же класс, таких как имена typedef).
Вот соответствующая цитата из стандарта C ++ 11, раздел 14.6.1p1:
Как и обычные (не шаблонные) классы, шаблоны классов имеют имя введенного класса (раздел 9). Введенное имя класса может использоваться как Имя Шаблона или Имя-типа. Когда это используется с Шаблон-аргумент-список, как Шаблон-аргумент для шаблона Шаблон-параметр, или как окончательный идентификатор в уточненный тип Спецификатор объявления шаблона класса друга, это относится к самому шаблону класса. В противном случае это эквивалентно Имя Шаблона с последующим Шаблон-параметры шаблона класса, заключенного в
<>
,
Очевидно, что это допустимо в C ++ 11.
Тем не менее, книга правильно описывает поведение C ++ 03:
Как и обычные (не шаблонные) классы, у шаблонов классов есть inject-class-name (раздел 9). Имя введенного класса может использоваться с или без Шаблон-аргумент-список. Когда он используется без Шаблон-аргумент-список, это эквивалентно имени введенного класса, сопровождаемого Шаблон-параметры шаблона класса, заключенного в
<>
, Когда он используется с Шаблон-аргумент-список, это относится к указанной специализации шаблона класса, которая может быть текущей специализацией или другой специализацией.
Помимо обратной логики, мы видим, что C ++ 03 содержал один случай, когда идентификатор ссылался на шаблон (когда были предоставлены аргументы шаблона), а C ++ 11 добавляет два дополнительных случая, один из которых влияет на этот код.
Таким образом, в C ++ 03 код был эквивалентен X<C<T>> c;
что является ошибкой, потому что X
Нужно передать шаблон, а не тип.
Итог: для изучения языка, особенно шаблонов, вам нужно книга по C ++ 11. Старые книги остаются полезными в отношении архитектуры и дизайна высокого уровня, но не могут объяснить языковые тонкости, которые изменились после их публикации.
Попытка ответить на ваши вопросы:
Различные компиляторы разных производителей и версий генерируют различную диагностику. Вы не можете полагаться на точные сообщения от одного к другому.
«Имя введенного класса» определено в стандарте согласно § 9, параграф 2:
Имя класса вставляется в область, в которой оно объявлено
сразу после того, как имя класса замечено. Имя класса также
вставляется в область действия самого класса; это известно как
впрыскивается класс имя. В целях проверки доступа
injected-class-name обрабатывается так, как если бы это было публичное имя члена.
спецификатор класса обычно называется определением класса. Класс
считается определенным после закрывающей скобки спецификатора класса
был замечен, хотя его функции-члены в целом еще не
определены. Необязательный атрибут-спецификатор-seq относится к классу;
атрибуты в атрибуте-определителе-seq после этого
рассматриваются атрибуты класса всякий раз, когда он назван.
C* a
такой же как C<T>* a
связано с контекстом: оба происходят в декларации C
, C ++ допускает «ярлык» в том смысле, что шаблонный класс может ссылаться на свою собственную реализацию без аргументов шаблона в контексте, в котором эти аргументы могут быть приняты (это один из них). Если вы хотите сослаться на экземпляр C
с другими аргументами шаблона, вы должны быть откровенны в этом.Причина, по которой вы получаете вашу конкретную ошибку в том, что C<void>
является неполным типом в то время, когда вы пытаетесь создать экземпляр C
с разными аргументами шаблона.
Глядя на код, я нахожу действительно странным то, что, как мне кажется, он должен компилироваться чисто, потому что вы никогда не создаете экземпляр любого шаблона, поэтому введите полноту C
не должно иметь значения.
Я думаю, что книга забыла *
там
C<void> *b;
В противном случае объявление недействительно даже без шаблонов. Потому что C еще не полностью объявлен, когда компилятор достигает этой строки. Это то же самое, что пытаться использовать класс forward forward.
Например, это недействительно.
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 дал ошибку; Между тем, перечислены другие ошибки
в книге не обнаружены? Почему, это возможная ошибка компилятора или
ошибка в книге?
Помимо того, что объяснялось выше, причина может заключаться в том, что книга относительно старая и с тех пор компиляторы изменились. Вы не можете ожидать, что сообщения об ошибках будут точно такими же.