Question

Recently, I was reading the book: C++ templates: the complete guide written by David Vandevoorde and Nicolai M. Josuttis.

Specifically about template parsing quoting from the book pp 126.

Class templates also have injected class names, However, they are stranger than ordinary injected class names: They can be followed by template arguments (in which case they are injected class template names ), but if they are not followed by template arguments they represent the class with its parameters as its arguments (or, for a partial specialization, its specialization arguments).

The relevant codes are excepted from the book as follows:

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;
}

The above code example tries to explain the whole quoted paragraph.

I compiled the above code in gcc 4.5.3, it outputs:

error: field ‘b’ has incomplete type

Therefore, I have the following questions:

  1. Why the compiler generates totally different error messages? the book says b is OK, but gcc gave the error; meanwhile, other errors listed in the book are not detected? Why, is this a possible compiler bug or error in book?
  2. What does injected class names mean? How can I identify what names are injected class names and what are not?
  3. why C*a is the same as C<T>* a? I tried to replace C*a with C<T>* a, no error is reported, so is C* a a shorthand for C<T>* a?

Thanks a lot!

Was it helpful?

Solution

Note: An injected-class-name is just the identifier used to declare the class (as opposed to other names which also refer to the same class, such as typedef names).

Here's the relevant quote from the C++11 Standard, section 14.6.1p1:

Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-name can be used as a template-name or a type-name. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier of a friend class template declaration, it refers to the class template itself. Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.

So clearly this is legal in C++11.

However, the book correctly describes the behavior of C++03:

Like normal (non-template) classes, class templates have an injected-class-name (clause 9). The injected-class-name can be used with or without a template-argument-list. When it is used without a template-argument-list, it is equivalent to the injected-class-name followed by the template-parameters of the class template enclosed in <>. When it is used with a template-argument-list, it refers to the specified class template specialization, which could be the current specialization or another specialization.

Aside from reversing the logic, we see that C++03 contained one case where the identifier referred to the template (when template arguments were provided) and C++11 adds two additional cases, one of which affects this code.

So in C++03, the code was equivalent to X<C<T>> c;, which is an error because X needs to be passed a template and not a type.

Bottom line: For learning about the language, templates especially, you need a book on C++11. Older books remain useful with respect to architecture and high level design, but can't explain language intricacies which changed after they were published.

OTHER TIPS

Trying to answer your questions:

  1. Different compilers across vendors and versions generate different diagnostics. You cannot rely on exact messages from one to another.

  2. "injected class name" is defined in the standard under §9, paragraph 2:

A class-name is inserted into the scope in which it is declared immediately after the class-name is seen. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name is treated as if it were a public member name. A class-specifier is commonly referred to as a class definition. A class is considered defined after the closing brace of its class-specifier has been seen even though its member functions are in general not yet defined. The optional attribute-specifier-seq appertains to the class; the attributes in the attribute-specifier-seq are thereafter considered attributes of the class whenever it is named.

  1. The reason C* a is the same as C<T>* a is due to context: both occur within the declaration of C. C++ allows a "shortcut" in that a templated class can be refer it's own instantiation without the template arguments within a context in which the arguments can be assumed (this is one of them). If you wanted to refer to an instantiation of C with different template arguments, you would need to be explicit about it.

The reason you're getting your particular error is that C<void> is an incomplete type at the time at which you're trying to instantiate a C with different template arguments.

What I find really odd, looking at the code, is that I think it should compile cleanly because you're never actually instantiating either template, so type completeness of C shouldn't matter.

I think the book forgot * there

C<void> *b;

Otherwise the declaration is not valid even with out templates. Because C is not completely declared yet when the compiler reaches that line. It is the same as trying to use a forward declare class.

For example this is not valid.

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

However this is valid.

class A {
    A *a;
};

Injected class name is when the template argument of a class is injected with in the class's own scope to its uses.

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

I think the book explains it completely.


so is C* a a shorthand for C<T>* a?

Only within the class's own scope. (including member definition scopes) e.g.

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

Why the compiler generates totally different error messages? the book says b is OK, but gcc gave the error; meanwhile, other errors listed in the book are not detected? Why, is this a possible compiler bug or error in book?

Apart from the things explained above, the reason can be that the book is relatively old and compilers have changed since then. You can't expect error messages to be exactly the same.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top