Оба шаблона обеспечивают точное соответствие аргументу — второй шаблон требует (допустимого) преобразования из массива в указатель, и это преобразование считается точным соответствием при подборе функции (см. раздел 6.6.1). Нешаблонная версия является подходящей, но требует пользовательского преобразования. Эта функция хуже точного соответствия, поэтому кандидатами остаются два шаблона. Как и прежде, версия Т* более специализирована, она и будет выбрана.

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

// преобразовать символьные указатели в строку и вызвать строковую

// версию debug_rep()

string debug_rep(char *p) {

 return debug_rep(string(p));

}

string debug_rep(const char *p) {

 return debug_rep(string(p));

}

Пропуск объявления может нарушить программу

Следует заметить, что для правильной работы версии char* функции debug_rep() объявление debug_rep(const string&) должно находиться в области видимости, когда эти функции определяются. В противном случае будет вызвана неправильная версия функции debug_rep():

template string debug_rep(const T &t);

template string debug_rep(T *p);

// следующее объявление должно быть в области видимости

// для правильного определения debug_rep(char *)

string debug_rep(const string &);

string debug_rep(char *p) {

 // если объявление для версии, получающей const string&, не находится

 // в области видимости, return вызовет call debug_rep(const Т&) с

 // экземпляром строки в параметре Т

 return debug_rep(string(p));

}

Обычно, если попытаться использовать функцию, которую забыли объявлять, код не будет откомпилирован. Но с функциями, которые перегружают шаблон функции, все не так. Если компилятор может создать экземпляр вызова из шаблона, то отсутствие объявления не будет иметь значения. В этом примере, если забыть объявлять версию функции debug_rep(), получающую строку, компилятор тихо создаст версию экземпляра шаблона, получающую const Т&.

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

Упражнения раздела 16.3

Упражнение 16.48. Напишите собственные версии функций debug_rep().

Упражнение 16.49. Объясните, что происходит в каждом из следующих вызовов:

template void f(Т);

template void f(const T*);

template void g(T);

template void g(T*);

int i = 42, *p = &i

const int ci = 0, *p2 = &ci

g(42); g(p); g(ci); g(p2);

f(42); f(p); f(ci); f(p2);

Упражнение 16.50. Определите функции из предыдущего упражнения так, чтобы они выводили идентификационное сообщение. Выполните код этого упражнения. Если вызовы ведут себя не так, как ожидалось, выясните почему.

<p><image l:href="#books.png"/>16.4. Шаблоны с переменным количеством аргументов</p>

Шаблон с переменным количеством аргументов (variadic template) — это шаблон функции или класса, способный получать переменное количество параметров. Набор таких параметров называется пакетом параметров (parameter pack). Есть два вида пакетов параметров: пакет параметров шаблона (template parameter pack), представляющий любое количество параметров шаблона, и пакет параметров функции (function parameter pack), представляющий любое количество параметров функции.

Для указания, что шаблону или функции представлен пакет параметров, используется многоточие. В списке параметров шаблона синтаксис class... или typename... означает, что следующий параметр представляет список любого количества типов; имя типа, сопровождаемое многоточием, представляет список из любого количества параметров значения заданного типа. Параметр в списке параметров функции, типом которого является пакет параметров шаблона, представляет собой пакет параметров функции. Например:

// Args - это пакет параметров шаблона; rest - пакет параметров функции

Перейти на страницу:

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