10.4. Явное задание аргументов шаблона A
10.4. Явное задание аргументов шаблона A
В некоторых ситуациях автоматически вывести типы аргументов шаблона невозможно. Как мы видели на примере шаблона функции min5(), если процесс вывода дает два различных типа для одного и того же параметра шаблона, то компилятор сообщает об ошибке – неудачном выводе аргументов.
В таких ситуациях приходится подавлять механизм вывода и задавать аргументы явно, указывая их с помощью заключенного в угловые скобки списка разделенных запятыми значений, который следует после имени конкретизируемого шаблона функции. Например, если мы хотим задать тип unsigned int в качестве значения аргумента шаблона T в рассмотренном выше примере использования min5(), то нужно записать вызов конкретизируемого шаблона так:
// конкретизируется min5( unsigned int, unsigned int )
min5 unsigned int ( ui, 1024 );
В этом случае список аргументов шаблона unsigned int явно задает их типы. Поскольку аргумент шаблона теперь известен, вызов функции больше не приводит к ошибке.
Обратите внимание, что при вызове функции min5() второй аргумент равен 1024, т.е. имеет тип int. Так как тип второго формального параметра функции при явном задании аргумента шаблона установлен в unsigned int, то второй фактический параметр функции приводится к типу unsigned int с помощью стандартного преобразования целых типов.
В предыдущем разделе мы говорили, что в процессе вывода аргументов шаблона к фактическим аргументам функции разрешается применять только ограниченное множество преобразований типов. Трансформация int в unsigned int в это множество не входит. Но если аргументы шаблона задаются явно, выполнять вывод типов не нужно, поскольку они уже зафиксированы. Следовательно, при явном задании аргументов шаблона для приведения типов фактических аргументов функции к типам формальных параметров можно применять любые стандартные преобразования.
Помимо разрешения любых преобразований фактических аргументов функции, явное задание аргументов шаблона помогает избежать и других проблем, встающих перед программистом. Рассмотрим следующую задачу. Мы хотим определить шаблон функции с именем sum() так, чтобы его конкретизация возвращала значения типа, достаточно большого для представления суммы двух значений любых двух типов, переданных в любом порядке. Как это сделать? Какой тип возвращаемого значения следует задать?
// каким должен быть тип возвращаемого значения: T или U
template class T, class U
??? sum( T, U );
В нашем случае нельзя использовать ни тот, ни другой параметрический тип, иначе мы неизбежно допустим ошибку:
char ch; unsigned int ui;
// ни T, ни U нельзя использовать в качестве типа возвращаемого значения
sum( ch, ui ); // правильно: U sum( T, U );
sum( ui, ch ); // правильно: T sum( T, U );
Решение заключается в том, чтобы ввести в шаблон третий параметр для обозначения типа возвращаемого значения:
// T1 не появляется в списке параметров шаблона функции
template class T1, class T2, class T3
T1 sum( T2, T3 );
Поскольку тип возвращаемого значения может отличаться от типов аргументов функции, T1 не упоминается в списке формальных параметров. Это потенциальная проблема, так как тип T1 не может быть выведен из фактических аргументов функции. Однако, если при конкретизации sum() мы зададим аргументы шаблона явно, то избегнем сообщения компилятора о невозможности вывести T1. Например:
typedef unsigned int ui_type;
ui_type calc( char ch, ui_type ui ) {
// ...
// ошибка: невозможно вывести T1
ui_type loc1 = sum( ch, ui );
// правильно: аргументы шаблона заданы явно
// T1 и T3 - это unsigned int, T2 - это char
ui_type loc2 = sum ui_type, ui_type ( ch, ui );
}
Не хватает возможности явно задать T1, но не T2 и T3, поскольку их можно вывести из аргументов функции при вызове.
При явном задании аргументов шаблона необходимо перечислять только те, которые не могут быть выведены автоматически. Но, как и в случае аргументов функции со значениями по умолчанию, опускать можно исключительно “хвостовые”:
// правильно: T3 - это unsigned int
// T3 выведен из типа ui
ui_type loc3 = sum ui_type, char ( ch, ui );
// правильно: T2 - это char, T3 - unsigned int
// T2 и T3 выведены из типа pf
ui_type (*pf)( char, ui_type ) = sum ui_type ;
// ошибка: опускать можно только “хвостовые” аргументы
ui_type loc4 = sum ui_type, , ui_type ( ch, ui );
Встречаются ситуации, когда невозможно вывести аргументы шаблона в контексте, где конкретизируется шаблон функции; следовательно, необходимо их явно задать. Именно выявление таких ситуаций и необходимость решить проблему послужила причиной поддержки явного задания аргументов шаблона в стандартном C++.
В следующем примере берется адрес конкретизированной функции sum() и передается в качестве аргумента перегруженной функции manipulate(). Как мы показали в разделе 10.2, невозможно понять, как именно нужно конкретизировать sum(), если есть только списки параметров функций manipulate(). Имеется две разных функции sum(), и обе удовлетворяют условиям вызова. Следовательно, вызов manipulate() неоднозначен. Одним из способов разрешения такой неоднозначности является явное приведение типов. Однако лучше использовать явное задание аргументов шаблона: оно позволяет указать, как именно конкретизировать sum(), и, следовательно, выбрать нужный вариант перегруженной функции manipulate(). Например:
template class T1, class T2, class T3
T1 sum( T2 op1, T3 op2 ) { /* ... */ }
void manipulate( int (*pf)( int,char ) );
void manipulate( double (*pf)( float,float ) );
int main()
{
// ошибка: какой из возможных экземпляров sum:
// int sum( int,char ) или double sum( float, float )?
manipulate( sum );
// берется адрес конкретизированного экземпляра
// double sum( float, float )
// вызывается: void manipulate( double (*pf)( float, float ) );
manipulate( sum double, float, float );
}
Отметим, что явное задание аргументов шаблона следует использовать только тогда, когда это абсолютно необходимо для разрешения неоднозначности или для конкретизации шаблона функции в контексте, где вывести аргументы невозможно. Во-первых, определение типов и значений аргументов шаблона проще оставить компилятору. А во-вторых, если мы модифицируем объявления в программе, так что типы аргументов функции при вызове конкретизированного шаблона изменятся, то компилятор автоматически скорректирует вызов без нашего вмешательства. С другой стороны, если аргументы шаблона заданы явно, необходимо проверить, что они по-прежнему отвечают новым типам аргументов функции. Поэтому мы рекомендуем избегать явного задания аргументов шаблона.
Упражнение 10.6
Назовите две ситуации, когда использование явного задания аргументов шаблона необходимо.
Упражнение 10.7
Пусть дано следующее определение шаблона функции sum():
template class T1, class T2, class T3
T1 sum( T2, T3 );
Какие из приведенных ниже вызовов ошибочны? Почему?
double dobj1, dobj2;
float fobj1, fobj2;
char cobj1, cobj2;
(a) sum( dobj1, dobj2 );
(b) sumdouble,double,double( fobj1, fobj2 );
(c) sumint( cobj1, cobj2 );
(d) sumdouble, ,double( fobj2, dobj2 );
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Явное связывание
Явное связывание Явное связывание, или связывание во время выполнения (run-time linking), требует, чтобы в программе содержались конкретные указания относительно того, когда именно необходимо загрузить или освободить библиотеку DLL. Далее программа получает адрес запрошенной
Пример: явное связывание функци и преобразования файлов
Пример: явное связывание функци и преобразования файлов Программа 2.4, предназначенная для преобразования кодировки текстовых файлов из ASCII в Unicode, вызывает функцию Asc2Un (программа 2.5), выполняющую обработку файла с использованием операций файлового ввода/вывода. Программа
R.5.4 Явное преобразование типа
R.5.4 Явное преобразование типа Явное преобразование типа можно задать с помощью функциональной записи (§R.5.2.3) или с помощью операции приведения.выражение-приведения: унарное-выражение (имя-типа) выражение-приведенияЗадание с помощью операции приведения используется
Использование аргументов
Использование аргументов Возможно, вы уже заметили, что большинство программ в Linux не интерактивны. Вы должны указать им какие-то параметры, в противном случае вы получите сообщение со списком возможных аргументов. Возьмем, к примеру, команду more. Если вы не укажете имя
Явное задание размера памяти
Явное задание размера памяти Иной путь выделения памяти заключается в явном ее задании. Во внешнем описании мы могли бы скачать: char m1[44] = "Только ограничьтесь одной строкой.";вместо char m1[ ] = "Только ограничьтесь одной строкой."; Можно быть уверенным, что число
2.1.1. Список аргументов
2.1.1. Список аргументов Для запуска программы достаточно ввести ее имя в командной строке. Дополнительные информационные элементы, передаваемые программе, также задаются в командной строке и отделяются от имени программы и друг от друга пробелами. Такие элементы
4.14.3. Явное преобразование типов
4.14.3. Явное преобразование типов Явное преобразование типов производится при помощи следующих операторов: static_cast, dynamic_cast, const_cast и reinterpret_cast. Заметим, что, хотя иногда явное преобразование необходимо, оно служит потенциальным источником ошибок, поскольку подавляет
Явное преобразование типов: CAST()
Явное преобразование типов: CAST() В тех случаях, когда Firebird не может выполнить неявное преобразование типов, вы должны выполнить явное преобразование типов посредством функции CAST(). Используйте CAST() для преобразования одного типа данных в другой в операторе SELECT обычно в
7.2.3 Явное Преобразование Типа
7.2.3 Явное Преобразование Типа Простое_имя_типа (#8.2), возможно, заключенное в скобки, за которым идет заключенное в скобки выражение (или спсок_выражений, если тип является классом с соответствующим образом описанным конструктором #8.5.5) влечет преобразование значения
Для чего необходимо явное создание объектов?
Для чего необходимо явное создание объектов? Объекты создаются явным образом. Объявление сущностиb: BOOK3не влечет за собой создание объекта во время выполнения, это происходит, когда некий элемент системы выполнит операциюcreate bЭто может показаться удивительным. Разве
Передача аргументов
Передача аргументов Один из аспектов нотации требует разъяснений: что происходит со значениями, переданными в качестве аргументов подпрограмме?Рассмотрим вызов в формеr (a1, a2, ..., an)соответствующий программеr (x1: T1, x2: T2, ..., xn: Tn) is ...где r может быть как функцией, так и
Явное переопределение
Явное переопределение Роль предложения redefine состоит в улучшении читаемости и надежности. Компиляторам, на самом деле, оно не нужно, так как в классе может быть лишь один компонент с данным именем, то объявленный в данном классе компонент, имеющий то же имя, что и компонент