Программирование >>  Полиморфизм без виртуальных функций в с++ 

1 ... 111 112 113 [ 114 ] 115 116 117 ... 144


some type[I]

СТ<Т>

СТ<1>

Т (*)(args)

some type (*) (args containing T) some type (*) (args containing I) ТС::* С Т: :*

Здесь Т - аргумент-тип шаблона, I - аргумент шаблона, который не является типом, СТ - имя ранее объявленного шаблона класса, args containing T -список аргументов, из которого можно определить Т, применяя эти правила, а С - имя класса. Теперь пример с функцией lookup () становится корректным. Пользователям не надо заучивать этот перечень, так как он просто формализует очевидный синтаксис.

Вот другой пример:

template<class Т, class U> void f(const T*, U(*)(U));

int g(int);

void h(const char* p) {

f(p,g); T - это char, и - это int f(p,h); ошибка: невозможно вывести U

Глядя на фактические параметры в первом вызове f (}, мы легко можем вывести фактические аргументы шаблона. Смотря на второй вызов f (), видим, что h () не соответствует образцу U (*) (U), поскольку типы аргумента и возвращаемого значения различаются.

В прояснении этого и многих других подобных вопросов существенную помощь оказал Джон Спайсер (John Spicer).

15.6.2. Задание аргументов шаблона функции

Проектируя шаблоны, я ду.мал о том, чтобы разрешить явное задание аргументов для шаблона функции точно так же, как можно задавать аргументы шаблонов классов. Например:

vector<int> v(10); класс, аргумент шаблона int sort<int>(v); функция, аргумент шаблона int

Однако от этой идеи пришлось отказаться, потому что в большинстве примеров явно задавать аргументы шаблона не требовалось. Также, я опасался неоднозначностей и трудностей при синтаксическом анализе. Скажем, как следует разбирать этот пример?

void g() {

f<l>(0); (f) < (1>(0)) или (f<l>) (0) ?



Теперь я пе считаю это проблемой. Если f - имя шаблона, то f < - начало квалифицированного имени и последующие лексемы должны интерпретироваться с учетом этого факта; в противном случае < означает меньше .

Явное задание может быть полезно, поскольку мы не можем вывести тип возвращаемого значения по вызову шаблона функции:

template<class Т, class U> Т convert(U u) { return u; }

void g(int i)

convert(i); ошибка: нельзя вывести Т convert<double>(i) ; 111 - double, U - int convert<char,double>(i); 111- char, U - double convert<char*,double>(i); 114:- char*, U - double

ошибка: нельзя преобразовать

double в char*

Как и для аргументов функции по умолчанию, в списке явно заданных аргументов шаблона можно опускать только крайние правые элементы.

Явное задание аргументов шаблона позволяет определить семейства функций преобразования и создания объектов. Явное преобразование, которое выполняет то, чего можно добиться одним лишь неявным преобразованием, например функция convert () в приведенном примере, достаточно часто требуется и хорошо подходит для включения в библиотеку. Еще один вариант - применить проверку, которая гарантировала бы, что для любого сужающего преобразования можно будет обнаружить ошибку во время выполнения.

Мы осознанно сделали похожим синтаксис новых операторов приведения (см. раздел 14.3) и явно квалифицированных вызовов шаблона функции. С помощью новых операторов приведения выражаются действия, которые нельзя описать другими средствами языка. Аналогичные операции, например convert (), можно выразить в виде шаблонов функций, поэтому они необязательно должны быть встроенными операторами.

Еще одно применение явно заданных аргументов шаблона функции - управление работой алгоритма за счет задания типа или значения локальной переменной. Например:

template<class ТТ, class АТ> void f(AT а) {

ТТ temp = а; используем ТТ для управления точностью вычислений

...

void g(Array<float>& a) {

f<float>(a); f<double>(a); f<Quad>(a);



Включение в С++ явного задания аргументов шаблона функции одобрено на заседании комитета в Сан-Хосе в ноябре 1993 г.

15.6.3. Перегрузка шаблона функции

Коль скоро существуют шаблоны функций, встает вопрос, как следует разрешать их перегрузку. Для данного средства допустимы только точные соответствия, а при разрешении перегрузки предпочтение отдается обычной функции с тем же именем:

Разрешение перегрузки для шаблонов функций и других функций с тем же именем выполняется в три этапа [ARM]:

□ поиск точного соответствия [ARM, § 13.2] среди функций; называние функции, если она найдена;

□ нахождение шаблона функции, из которого можно инстанцировать функцию, точно соответствующую параметрам вызова; вызов этой функции, если шаблон найден;

□ попытка применить обычную перегрузку [ARM, § 13.2] для функций; вызов функции, если она найдена.

Если соответствие не найдено, вызов считается ошибкой. Если на первом шаге отыскивается более одного соответствия, то вызов неоднозначен и также считается ошибкой .

Теперь такой подход кажется узкоспециализированны.м. Хотя он и работает, но служит почвой для .многих мелких сюрпризов и неприятностей.

Уже в то вре.мя мне было ясно, что лучше как-то унифицировать правила для обычных функций и шаблонов. Но я не знал как. Вот приблизительный вариант альтернативного подхода, сформулированный Эндрю Кенигом:

Для данного вызова найти множество функций, которые в принципе можно было бы подставить. В стандартном случае оно будет содержать функции, сгенерированные из разных шаблонов. Применить обычные правила разрешения к этому множеству функций .

Такое решение позволило бы применять преобразования к аргументам шаблонов функций, и мы получили бы общую схему перегрузки для любых функций. Например:

template<class Т> class В {/*...*/ }; template<class Т> class D : public В<Т> { /* ... */ };

template<class Т> void f(B<T>*);

void g(B<int>* pb, D<int>* pd) {

f(pb); f<int>(pb)

f(pd); f<int>((B<int>*)pd);

используется стандартное преобразование

Это необходимо для того, чтобы шаблоны функций правильно взаимодействовали с наследованием. Другой пример:

template<class Т> Т тах(Т,Т); const int s = 7;



1 ... 111 112 113 [ 114 ] 115 116 117 ... 144

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика