Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
B.Eckel - Thinking in C++, Vol.2, 2nd edition.pdf
Скачиваний:
50
Добавлен:
08.05.2013
Размер:
2.09 Mб
Скачать

3:Templates in depth

Nontype template arguments

Here is a random number generator class that always produces a unique number and overloads operator( ) to produce a familiar function-call syntax:

//: C03:Urand.h

// Unique random number generator #ifndef URAND_H

#define URAND_H #include <cstdlib> #include <ctime>

template<int upperBound> class Urand {

int used[upperBound]; bool recycle;

public:

Urand(bool recycle = false);

int operator()(); // The "generator" function

};

template<int upperBound> Urand<upperBound>::Urand(bool recyc)

: recycle(recyc) {

memset(used, 0, upperBound * sizeof(int)); srand(time(0)); // Seed random number generator

}

template<int upperBound>

121

int Urand<upperBound>::operator()() { if(!memchr(used, 0, upperBound)) {

if(recycle)

memset(used,0,sizeof(used) * sizeof(int)); else

return -1; // No more spaces left

}

int newval;

while(used[newval = rand() % upperBound]) ; // Until unique value is found

used[newval]++; // Set flag return newval;

}

#endif // URAND_H ///:~

The uniqueness of Urand is produced by keeping a map of all the numbers possible in the random space (the upper bound is set with the template argument) and marking each one off as it’s used. The optional constructor argument allows you to reuse the numbers once they’re all used up. Notice that this implementation is optimized for speed by allocating the entire map, regardless of how many numbers you’re going to need. If you want to optimize for size, you can change the underlying implementation so it allocates storage for the map dynamically and puts the random numbers themselves in the map rather than flags. Notice that this change in implementation will not affect any client code.

Default template arguments The typename keyword

Consider the following:

//: C03:TypenamedID.cpp

//Using 'typename' to say it's a type,

//and not something other than a type

template<class T> class X {

// Without typename, you should get an error: typename T::id i;

public:

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

};

Chapter 15: Multiple Inheritance

122

class Y { public:

class id { public:

void g() {}

};

};

int main() { Y y;

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 be a member object of T, in which case you can perform operations on id directly, but you couldn’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, when it sees an identifier in a template, the compiler has the option of treating that identifier as a type or as something other than a type, then 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. Thus, the compiler gets confused when we pretend it’s a type.

The typename keyword tells the compiler to interpret a particular name as a type. It must be used for a name that:

1.Is a qualified name, one that is nested within another type.

2.Depends on a template argument. That is, a template argument is somehow involved in the name. The template argument causes the ambiguity when the compiler makes the simplest assumption: that the name refers to something other than 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 even in places where you think that the compiler ought to be able to figure out the right way to interpret the name on its own. In the above example, when the compiler sees 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 your type is a qualified name that involves a template argument, you must use typename.

Chapter 15: Multiple Inheritance

123

Соседние файлы в предмете Численные методы
  • #
    08.05.20133.99 Mб22A.Menezes, P.van Oorschot,S.Vanstone - HANDBOOK OF APPLIED CRYPTOGRAPHY.djvu
  • #
  • #
    08.05.20135.91 Mб24B.Eckel - Thinking in Java, 3rd edition (beta).pdf
  • #
  • #
    08.05.20136.09 Mб17D.MacKay - Information Theory, Inference, and Learning Algorithms.djvu
  • #
    08.05.20133.85 Mб15DIGITAL Visual Fortran ver.5.0 - Programmers Guide to Fortran.djvu
  • #
    08.05.20131.84 Mб12E.A.Lee, P.Varaiya - Structure and Interpretation of Signals and Systems.djvu