. template как квалификатор
И снова об этом загадочном квалификаторе.
В данном разделе д-р Страуструп привел пример его использования с функцией-членом шаблоном. А что, если нам нужно вызвать статическую функцию-член или функцию-друга? Полный пример будет выглядеть следующим образом: template <class T> void get_new3(); // (1)
template <class Allocator> void f(Allocator& m) { int* p1= m.template get_new1<int>( ); int* p2=Allocator::template get_new2<int>(m); int* p3= get_new3<int>(m); }
struct Alloc { template <class T> T* get_new1() { return 0; }
template <class T> static T* get_new2(Alloc&) { return 0; }
template <class T> friend T* get_new3(Alloc&) { return 0; } };
int main() { Alloc a; f(a); }
Итак:
get_new1 --- это функция-член, для вызова которой в данном случае обязательно должен быть использован квалификатор template. Дело в том, что в точке определения f класс Allocator является всего лишь именем параметра шаблона и компилятору нужно подсказать, что данный вызов -- это не (ошибочное) выражение (m.get_new1) < int...
get_new2 -- это статическая функция-член, при вызове из f, ее имя должно быть предварено все тем же квалификатором template по тем же причинам.
А вот get_new3 -- друг класса Alloc, привносит в наш пример некоторые проблемы. Дело в том, что он используется в f до его определения в классе Alloc (точно так же, как я использую до их определения функции get_new1 и get_new2). Чтобы определение f было корректным, мы должны гарантировать, что имя get_new3 известно в точке определения f как имя функции-шаблона. Дабы не ограничивать общность f, я не использовал в точке (1) прототип конкретной get_new3 -- друга класса Alloc, а просто описал (даже не определил!) некоторую функцию-шаблон get_new3. Очевидно, что она не может быть использована в f -- она просто делает вызов p3=get_new3<int>(m);
легальным, внося в область видимости нужное имя-шаблон. Обратите внимание, что описанная в точке (1) функция get_new3 не имеет параметров и не возвращает никакого значения. Это сделано для того, чтобы она не принималась во внимание при выборе подходящей (возможно перегруженной) get_new3, в точке ее вызова в функции f.
Как видите, в случае функции-друга я был вынужден использовать не совсем красивый трюк, т.к. C++ не позволяет мне прямо выразить то, что я хотел, а именно: написать p3=template get_new3<int>(m);
К сожалению, приходится констатировать, что использование квалификатора template не было в достаточной мере продумано комитетом по стандартизации C++.