- •Ооп: Лекция 6. Шаблоны. Основы обобщенного программирования.
- •Введение в шаблоны - шаблоны функций
- •Автоматический вывод типов в шаблонах
- •Шаблоны классов
- •Технические проблемы использования шаблонов
- •Константы в аргументах шаблона
- •Компоновка шаблонов
- •Пример: собственная шаблонная реализация std::initializer_list
- •Пример: шаблонная реализация связного списка
- •Обобщенные алгоритмы
- •Полные примеры из лекции
Автоматический вывод типов в шаблонах
Рассмотрим еще один пример шаблона - функцию сложения двух операндов:
template< typename T > T add( T a, T b )
{
return a + b;
}
Отличие этого примера от abs в том, что предполагается передача двух аргументов одного и того же типа T. Если не указывать аргумент шаблона в явном виде, не действуют никакие правила преобразования типов, как для обычных функций. Компилятор позволяет в таком случае только однозначное соответствие во избежание недоразумений. Первые два примера компилируются, так как оба операнда имеют одинаковый однозначный тип, а последний - не компилируется, поскольку тип T из такого контекста неоднозначен:
int main ()
{
int x = add( 2, 3 ); // ОК: add< int >
double y = add( 2.5, 3.5 ); // ОК: add< double >
double z = add( 2, 2.5 ); // Ошибка
}
error C2782: 'T add(T,T)' : template parameter 'T' is ambiguous
see declaration of 'add'
could be 'double'
or 'int'
Проблему можно решить явным указанием типа либо преобразованием фактического аргумента:
double z1 = add< int >( 2, 2.5 ); // ОК: add< int >, округление 2-го операнда
double z2 = add< double >( 2, 2.5 ); // ОК: add< double >
double z3 = add( ( double ) 2, 2.5 ); // ОК: add< double >
double z4 = add( 2, ( int ) 2.5 ); // ОК: add< int>
Чтобы разрешить работу с несколькими типами сразу, шаблоны могут содержать несколько аргументов. Например:
template< typename RT, typename T1, typename T2 > RT add ( T1 a, T2 b )
{
return a + b;
}
Для явного указания аргументов такого шаблона при вызове следует перечислить фактические типы в угловых скобках через запятую:
short x = 20000;
int result = add< int, char, short >( ‘a’, x );
Автоматический вывод типов может быть легко получен для передаваемых аргументов:
short x = 20000;
int result = add< int >( ‘a’, x );
До С++’11 в подобных задачах тип результата приходилось указывать в явном виде в любом случае. Еще одну хитрость можно произвести на основе новейшей конструкции языка - операторе decltype. Такой оператор можно применить к любому выражению и использовать в контекстах, в которых обычно ожидается тип данных. Например:
decltype(2.5) x;
создаст переменную x с типом double. В обычном коде применение такого оператора значительно снижает читабельность и является ничем не оправданным. Однако в коде шаблонов такой оператор может найти свое разумное применение, если в качестве выражений подавать конструкции, тип которых не известен без фактического инстанцирования. В решаемой задаче сложения при помощи оператора decltype можно записать тип для выражения суммы двух аргументов:
template< typename T1, typename T2 >
decltype( T1() + T2() ) add ( T1 a, T2 b )
{
return a + b;
}
Разберем данное выражение подробнее:
decltype( T1() + T2() )
Во время компиляции (не во время выполнения!) создаются значения по умолчанию для типа T1 и типа T2. Для числовых типов это приведет к созданию нулевых значений, но соответствующих типов. Далее формируется выражение сложения. Его значение никого не интересует, зато компилятор может при помощи оператора decltype автоматически вывести его тип, в соответствии с правилами языка. В итоге, функция add получает правильный возвращаемый тип без явного указания. Наконец, правильно и без лишнего синтаксического мусора будет работать такой клиентский код:
short x = 20000;
int result = add( ‘a’, x );
Использование оператора decltype в качестве возвращаемого типа функций визуально нравится далеко не всем, поскольку трудно воспринять границу между возвращаемым типом и названием функции. В связи с этим, в новейшем стандарте С++14 была предложена альтернативная запись, подчеркивающая автоматический вывод возвращаемого типа, в которой выражение на основе decltype указывается не в начале объявления, а в конце после аргументов:
template< typename T1, typename T2 >
auto add ( T1 a, T2 b ) -> decltype( T1() + T2() )
{
return a + b;
}
Какую форму записи выбрать - дело вкуса программиста. Поведение обеих форм эквивалентно.
