Выбрать главу

The typename keyword

Consider the following:.

//: C05:TypenamedID.cpp

//{-bor}

// Uses 'typename' as a prefix for nested types

template<class T> class X {

  // Without typename, you should get an error:

  typename T::id i;

public:

  void f() { i.g(); }

};

class Y {

public:

  class id {

  public:

    void g() {}

  };

};

int main() {

  X<Y> xy;

  xy.f();

} ///:~

The template definition assumes that the class T that you hand it must have a nested identifier of some kind called id. But id could also be a static data member of T, in which case you can perform operations on id directly, but you can’t "create an object" of "the type id.".

However, that’s exactly what is happening here: the identifier id is being treated as if it were actually a nested type inside T. In the case of class Y, id is in fact a nested type, but (without the typename keyword) the compiler can’t know that when it’s compiling X.

If the compiler has the option of treating an identifier as a type or as something other than a type when it sees an identifier in a template, it will assume that the identifier refers to something other than a type. That is, it will assume that the identifier refers to an object (including variables of primitive types), an enumeration, or something similar. However, it will not–cannot–just assume that it is a type.

Because the default behavior of the compiler is to assume that a name that fits the above two points is not a type, you must use typename for nested names (except in constructor initializer lists, where it is neither needed nor allowed). In the above example, when the compiler sees template T::id, it knows (because of the typename keyword) that id refers to a nested type and thus it can create an object of that type.

The short version of the rule is: if a type referred to inside template code is qualified by a template type parameter, it should be preceded by the typename keyword, unless it appears in a base class specification or initializer list in the same scope (in which case you must not).

All the above explains the use of the typename keyword in the program TempTemp4.cpp. Without it, the compiler would assume that the expression Seq<T>::iterator is not a type, but we were using it to define the return type of the begin( ) and end( ) member functions.

The following example, which defines a function template that can print any standard C++ sequence, shows a similar use of typename.

//: C05:PrintSeq.cpp

//{-msc}

// A print function for standard C++ sequences

#include <iostream>

#include <list>

#include <memory>

#include <vector>

using namespace std;

template<class T, template<class U, class = allocator<U> >

   class Seq>

void printSeq(Seq<T>& seq) {

  for (typename Seq<T>::iterator b = seq.begin();

       b != seq.end();)

    cout << *b++ << endl;

}

int main() {

  // Process a vector

  vector<int> v;

  v.push_back(1);

  v.push_back(2);

  printSeq(v);

  // Process a list

  list<int> lst;

  lst.push_back(3);

  lst.push_back(4);

  printSeq(lst);

} ///:~

Once again, without the typename keyword the compiler will interpret iterator as a static data member of Seq<T>, which is a syntax error, since a type is required.

Typedef-ing a typename

It’s important not to assume that the typename keyword creates a new type name. It doesn’t. Its purpose is to inform the compiler that the qualified identifier is to be interpreted as a type. A line that reads:.

typename Seq<T>::iterator It;

causes a variable named It to be declared of type Seq<T>::iterator. If you mean to create a new type name, you should use typedef, as usual, as in:.

typedef typename Seq<It>::iterator It;

Using typename instead of class

Another role of the typename keyword is to provide you the option of using typename instead of class in the template argument list of a template definition. To some, this produces clearer code (your mileage may vary):.

//: C05:UsingTypename.cpp

// Using 'typename' in the template argument list

template<typename T> class X { };

int main() {

  X<int> x;

} ///:~

You probably won’t see a great deal of code that uses typename in this fashion, since the keyword was added to the language a relatively long time after templates were introduced.

Using the template keyword as a hint

Just as the typename keyword helps the compiler in situations in which a type identifier is not expected, there is also a potential difficulty with tokens that are not identifiers, such as the < and > characters; sometimes they represent the less-than or greater-than symbols, and sometimes they delimit template parameter lists. As an example, we’ll once more use the bitset class:.

//: C05:DotTemplate.cpp

// Illustrate the .template construct

#include <bitset>

#include <cstddef>

#include <iostream>

#include <string>

using namespace std;

template<class charT, size_t N>

basic_string<charT> bitsetToString(const bitset<N>& bs) {

  return bs. template to_string<charT, char_traits<charT>,