|
Программирование >> Разработка устойчивых систем
Из презентации Херба Саттера (Herb Sutter). Этот интерфейс используется некоторыми компиляторами, в том числе Comeau С++. объявляться с префиксом std::. Однако некоторые элементы из пространства имен std остались неуточненными. Видите, какие именно? Не указано, какие операторные функции должны использоваться в программе. Мы хотим, чтобы происходило следующее: std;: operator ( std:: operator ( std:: cout. s). std:: endl): Ho вводить эту конструкцию соверщенно не хочется! Исходная команда вывода работает именно так, как мы хотим, благодаря механизму ADL. Если в программе присутствует неуточненный вызов функции, объявление которой отсутствует в обычной области видимости, то поиск подходящих объявлений функций производится в пространствах имен каждого из ее аргументов. В исходной команде первой вызывается функция operator (std:: cout. s): Поскольку такая функция отсутствует в области видимости исходного фрагмента, компилятор замечает, что первый аргумент функции (std::cout) принадлежит пространству имен std; он включает это пространство имен в список поиска уникальных функций, лучще всего соответствующих сигнатуре operator (std::ostream& std::string). Такое объявление находится в пространстве имен std через заголовок <string>. Без ADL работать с пространствами имен было бы крайне неудобно. Обратите внимание: механизм ADL обычно анализирует все объявления с указанным именем из всех доступных пространств имен. При отсутствии единого оптимального совпадения возникает неоднозначность. Чтобы запретить применение ADL, заключите имя функции в круглые скобки: (f)(x. у): Подавление механизма ADL Теперь рассмотрим следующую программу: : С05:Lookup.срр Правильно работает только в EDG и Metrowerks (со специальным ключом) #1nclude <iostream> using std::cout: using std::endl: void f(double) { cout f(double) endl: } tempiate<class T> class X { public: void g() { f(l): } void f(int) { cout f(int) endl: } int mainO { X<int>().g(); } /:- Из всех имеющихся у нас компиляторов программа работала правильно только в интерфейсе Edison Design Group, хотя в некоторых компиляторах (например, Metrowerks) правильное поведение при поиске активизируется специальным Также основанный на одном из примеров Херба Саттера. ключом. Поскольку f является независимым именем, которое может быть разрешено на ранней стадии по контексту определения шаблона, когда в области видимости находится только f(double), результат должен выглядеть так: f(double) К сожалению, многие существующие программы зависят от этого нестандартного поведения с привязкой вызова f(l) из д() к более позднему определению f(int), поэтому производители компиляторов не хотят вносить изменения. Рассмотрим более подробный пример: : C05:Lookup2.cpp {-bor}{-g++}{-dmc} Microsoft: необходим ключ -Za (режим ANSI) #incl ude <algorithm> #incl ude <iostream> #incl ude <typeinfo> using std::cout: using std::endl: void go { cout global gO endl: } template <class T> class Y { public: void gO { cout Y< typeid(D.nameO >::g() endl; } void hO { cout Y< typeid(T).nameO >::h() endl: typedef int E; typedef double E: tempiate<class T> void swap(T& tl. T& t2) { cout global swap endl; T temp = tl; tl - t2; t2 = temp; tempiate<class T> class X : public Y<T> { public: E fO { gO: this->h(); T tl - TO. t2 - T(l); cout tl endl; swap(tl. t2); std::swap(tl. t2): cout typeid(E).nameO endl: return E(t2); int mainO { X<int> x; cout X.fO endl: } /:- Результат выполнения программы будет выглядеть так: global gO Y<int>::h() global swap double Теперь посмотрим на объявления в X::f(). Е, тип возвращаемого значения X::f(), не является зависимым именем, поэтому его поиск производится при обработке шаблона. В результате обнаруживается определение типа, в соответствии с которым Е определяется как double. На первый взгляд это выглядит странно, поскольку в нешаблонных классах сначала будет найдено определение Е в базовом классе, но таковы правила (базовый класс Y является зависимым базовым классом, поэтому в момент определения шаблона поиск в нем производиться не может). Вызов д() тоже независим, поскольку он не содержит упоминаний Т. Если бы функция д() имела параметры, относящиеся к типу класса, определенного в другом пространстве имен, то механизм ADL начал бы работать, так как в области видимости нет определения д() с параметрами. В итоге для вызова устанавливается соответствие с глобальным объявлением д(). Вызов this->h() является уточненным; уточнение (this) соответствует текущему объекту, относящемуся к типу X, который, в свою очередь, зависит от имени Y<T> из-за наследования. В X не существует функции h(), поэтому поиск выполняется в области видимости базового класса X, то есть Y<T>. Данное имя является зависимым и потому рассматривается на стадии специализации при наличии надежной информации о У<Т> (включая все потенциальные специализации, которые могли быть написаны после определения X), поэтому в конечном счете вызывается Y<int>::h(). Объявления tl и t2 являются зависимыми. Вызов operator (couttl) зависим, потому что tl относится к типу Т. Поиск выполняется позднее, когда Т соответствует int, а оператор для int находится в std. Неуточненный вызов swap() зависим, потому что его аргументы относятся к типу Т. В конечном счете это приводит к специализации глобальной функции swap(int&,int&). Уточненный вызов std::swap() не является зависимым из-за присутствия фиксированного пространства имен std. Компилятор знает, что нужное объявление нужно искать в std (чтобы уточненное имя считалось зависимым, в квалификаторе слева от парного двоеточия должен упоминаться параметр шаблона). Позднее шаблон функции std::swap() генерирует std::swap(int&,int&) на стадии специализации. Других зависимых имен в X<T>::f() нет. Подведем итог: поиск зависимых имен осуществляется в момент специализации; исключение из этого правила составляют неуточненные зависимые имена, поиск которых начинается с определения шаблона. Поиск независимых имен в шаблонах производится рано, при обработке определения шаблона (при необхо-
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |