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

void f(int) { cout << "f(int)\n"; }

int main() {

  X().g();

}

The only compiler we have that gets this correct right out of the box is the Edison Design Group front end. (A number of compilers use this front end, including Comeau C++.) The output should be:.

f(double)

because f is a non-dependent name that can be resolved early by looking in the context where the template is defined, when only f(double) is in scope. Unfortunately, there is a lot of code in existence that depends on the non-standard behavior of binding the call to f(1) inside g( ) to the latter f(int), so compiler writers have been reluctant to make the change. (Some compilers, such as the Metrowerks compiler, have an option to enable the correct lookup behavior.).

Here is a more detailed example, also based on an example from Herb Sutter:

//: C05:Lookup2.cpp

//{-bor}

//{-g++}

// Microsoft: use option –Za (ANSI mode)

#include

#include

using std::cout;

using std::endl;

void g() { cout << "global g()\n"; }

template

class Y {

public:

  void g() { cout << "Y<" << typeid(T).name()

   << ">::g()\n"; }

  void h() { cout << "Y<" << typeid(T).name()

   << ">::h()\n"; }

  typedef int E;

};

typedef double E;

template

void swap(T& t1, T& t2) {

  cout << "global swap\n";

  T temp = t1;

  t1 = t2;

  t2 = temp;

}

template

class X : public Y {

public:

  E f() {

    g();

    this->h();

    T t1 = T(), t2 = T(1);

    cout << t1 << endl;

    swap(t1, t2);

    std::swap(t1, t2);

    cout << typeid(E).name() << endl;

    return E(t2);

  }

};

int main() {

  X x;

  cout << x.f() << endl;

} ///:~

The output from this program should be:

global g()

Y::h()

0

global swap

double

1

Looking at the declarations inside of X::f( ), we observe the following:

·         The return type of X::f( ), which is E, is not a dependent name, so it is looked up when the template is parsed, and the typedef naming E as a double is found. This may seem strange, since with non-template classes the declaration of E in the base class would be found first, but those are the rules. (The base class, Y, is a dependent base class, so it can’t be searched at template definition time).

·         The call to g( ) is also non-dependent, since there is no mention of T. If g had parameters that were of class type of defined in another namespace, ADL would take over, since there is no g with parameters in scope. As it is, this call matches the global declaration of g( ).

·         The call this->h( ) is a qualified name, and the object that qualifies it (this) refers to the current object, which is of type X, which in turn depends on the name Y by inheritance. There is no function h( ) inside of X, so lookup will naturally want to search the scope of X’s base class, Y. Since this is a dependent name, it is looked up at instantiation time, when Y can be reliably known (including any potential specializations that might have been written after the definition of X); so it calls Y::h( ).

·         The declarations of t1 and t2 are dependent, of course.

·         The call to operator<<(cout, t1) is dependent, since t1 is of type T. This is looked up later when T is int, and the inserter for int is found in std.

·         The unqualified call to swap( ) is dependent because its arguments are of type T. This ultimately causes a global swap(int&, int&) to be instantiated, of course.

·         The qualified call to std::swap( ) is not dependent, because std is a fixed namespace; so the compiler knows to look there for the proper declaration. (The qualifier on the left of the "::" must mention a template parameter for a qualified name to be considered dependent.) The std::swap( ) function template later generates std::swap(int&, int&), at instantiation time. No more dependent names remain in X::f( ).

To clarify and summarize: name lookup is done at the point of instantiation if the name is dependent, except that for unqualified dependent names the normal name lookup is also attempted early, at the point of definition. All non-dependent names in templates are looked up early, at the time the template definition is parsed. (If necessary, another lookup occurs at instantiation time, when the type of the actual argument is known.).

(Whew!) If you have studied this example to the point that you understand it, prepare yourself for yet another surprise in the next section when friend declarations enter the picture.

<p>Templates and friends</p>
Перейти на страницу:

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

3ds Max 2008
3ds Max 2008

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

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

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