B.1. The IssueTemplate compilation has two phases: The first occurs at the template's point of definition, and the second at each of its points of instantiation. According to the C++ standard, a template must be completely checked for syntactic correctness at its point of definition,[2] so its author can know that it is well-formed long before it is instantiated:
template <class ForwardIterator1, class ForwardIterator2> void iter_swap(ForwardIterator1 i1, ForwardIterator2 i2) { T tmp = *i1; // error: unknown identifier T *i1 = *i2; *i2 = tmp; } B.1.1. Problem OneDuring standardization, the committee discovered several cases for which it was impossible to do a full syntactic check at a template's point of definition. For example, consider this translation unit containing a definition of iter_swap: double const pi = 3.14159265359; template <class T> struct iterator_traits; // declaration only template <class FwdIterator1, class FwdIterator2> void iter_swap(FwdIterator1 i, FwdIterator2 j) { iterator_traits<FwdIterator1>::value_type* pi = &*i; ...continued... } The compiler has to check iter_swap for syntax errors, but it hasn't seen a definition of iterator_traits yet. Its ::value_type could be a type, in which case the highlighted line is a valid declaration. However, it could also turn out to be an enum value: template <class T> struct iterator_traits { enum { value_type = 0 }; }; in which case the first line of iter_swap is nonsense. It's tempting to think that the compiler should deduce that value_type must be a type, because there's no way the first line of iter_swap could be valid otherwise. Consider this counterexample, though:
class number
{
public:
template <class U>
number& operator=(U const&);
int& operator*() const;
};
number operator*(number, double);
template <class T>
struct iterator_traits
{
static number value_type;
};
In this case, iter_swap might still be validits first line would multiply a number by pi, and then assign into it: (iterator_traits<FwdIterator1>::value_type * pi) = &*i; but if so the syntactic structure of iter_swap would be completely different. It's also tempting to think that the compiler could syntax-check iter_swap if it had already seen the definition of iterator_traits, but specializations scuttle that possibility: any given instance of iterator_traits could be defined differently: template <> struct iterator_traits<int*> { static void* value_type; }; The problem is that iterator_traits<FwdIterator1>::value_type is a dependent name. The syntactic role it plays depends on what FwdIterator1 turns out to be, and can never be known at iter_swap's point of definition. B.1.2. Disambiguating TypesThe typename keyword tells the compiler that a dependent name denotes a dependent type: template <class FwdIterator1, class FwdIterator2> void iter_swap(FwdIterator1 i, FwdIterator2 j) { typename iterator_traits<FwdIterator1>::value_type* pi = &*i; ...continued... } Now the syntactic role of iterator_traits<FwdIterator1>::value_type is clear, and the compiler knows that pi denotes a pointer for the rest of the body of iter_swap. If we don't write typename, the compiler assumes that value_type denotes a non-type, and pi denotes a const double in iter_swap. B.1.3. Using class Versus typenameAs we mentioned earlier, the following two declarations are equivalent: template <class T> class vector; template <typename T> class vector; The argument in favor of using typename is that it's conceptually accurate: class seems to indicate that the argument must be a class type, when in fact any type will do. There's certainly nothing wrong with vector<int>! To understand the argument in favor of using class, consider the use of typename in the following declaration: template <typename T, typename T::value_type> struct sqrt_impl; You may have missed this, but only the first use of typename is declaring a type parameter: The second typename is declaring that T::value_type is a type. Therefore, the second parameter to sqrt is a value of type T::value_type. If that seems confusing, we can't blame you. Maybe this equivalent declaration will help clarify it:
template <class T, typename T::value_type n>
struct sqrt_impl;
If so, you understand the argument for using class to declare template type parameters: It's less confusing if typename is only used to mean one thing (syntax disambiguation) in template parameter lists. We're not going to tell you which practice you should use; people of goodwill can disagree about whether conceptual accuracy is more important than avoiding confusion in the rare cases where typename is used in non-type parameter declarations. In fact, the authors of this book disagreed, which is why you'll see class here and typename in the MPL reference manual. B.1.4. Problem TwoThe same kind of issue arises with template members:
double const pi = 3.14159265359;
template <class T>
int f(T& x)
{
return x.convert<3>(pi);
}
T::convert might be a member function template, in which case the highlighted code passes pi to a specialization of convert<3>. It could also turn out to be a data member, in which case f returns (x.convert < 3 ) > pi. That isn't a very useful calculation, but the compiler doesn't know it. B.1.5. Disambiguating TemplatesThe template keyword tells the compiler that a dependent name is a member template:
template <class T>
int f(T& x)
{
return x.template convert<3>(pi);
}
If we omit template, the compiler assumes that x.convert does not name a template, and the < that follows it is parsed as the less-than operator. ![]() |