Читаем Thinking In C++. Volume 2: Practical Programming полностью

Both the definition of seq in Container and theData in main( ) use the default. The only way to use something other than the default value is as the previous program (TempTemp2.cpp) illustrated. This is the only exception to the rule stated earlier that default arguments should appear only once in a compilation unit.

Since the standard sequence containers (vector, list, and deque, discussed in depth in Chapter 7) have a default allocator argument, the technique shown above is helpful should you ever want to pass one of these sequences as a template parameter. The following program passes a vector and then a list to two instances of Container.

//: C05:TempTemp4.cpp

//{-bor}

//{-msc}

// Passes standard sequences as template arguments

#include

#include

#include   // Declares allocator

#include

using namespace std;

template >

     class Seq>

class Container {

  Seq seq; // Default of allocator applied implicitly

public:

  void push_back(const T& t) { seq.push_back(t); }

  typename Seq::iterator begin() { return seq.begin(); }

  typename Seq::iterator end() { return seq.end(); }

};

int main() {

  // Use a vector

  Container theData;

  theData.push_back(1);

  theData.push_back(2);

  for(vector::iterator p = theData.begin();

      p != theData.end(); ++p) {

    cout << *p << endl;

  }

  // Use a list

  Container theOtherData;

  theOtherData.push_back(3);

  theOtherData.push_back(4);

  for(list::iterator p2 = theOtherData.begin();

      p2 != theOtherData.end(); ++p2) {

    cout << *p2 << endl;

  }

} ///:~

In this case we name the first parameter of the inner template Seq (with the name U), because the allocators in the standard sequences must themselves be parameterized with the same type as the contained objects in the sequence. Also, since the default allocator parameter is known, we can omit it in the subsequent references to Seq, as we did in the previous program. To fully explain this example, however, we have to discuss the semantics of the typename keyword.

<p>The typename keyword</p>

Consider the following:.

//: C05:TypenamedID.cpp

//{-bor}

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

template 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 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::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

#include

#include

#include

using namespace std;

template >

   class Seq>

void printSeq(Seq& seq) {

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

       b != seq.end();)

    cout << *b++ << endl;

}

int main() {

Перейти на страницу:

Похожие книги

3ds Max 2008
3ds Max 2008

Одни уверены, что нет лучшего способа обучения 3ds Мах, чем прочитать хорошую книгу. Другие склоняются к тому, что эффективнее учиться у преподавателя, который показывает, что и как нужно делать. Данное издание объединяет оба подхода. Его цель – сделать освоение 3ds Мах 2008 максимально быстрым и результативным. Часто после изучения книги у читателя возникают вопросы, почему не получился тот или иной пример. Видеокурс – это гарантия, что такие вопросы не возникнут: ведь автор не только рассказывает, но и показывает, как нужно работать в 3ds Мах.В отличие от большинства интерактивных курсов, где работа в 3ds Мах иллюстрируется на кубиках-шариках, данный видеокурс полностью практический. Все приемы работы с инструментами 3ds Мах 2008 показаны на конкретных примерах, благодаря чему после просмотра курса читатель сможет самостоятельно выполнять даже сложные проекты.

Владимир Антонович Верстак , Владимир Верстак

Программирование, программы, базы данных / Программное обеспечение / Книги по IT