Отказ от ответственности: Статья была начата еще в феврале, но по независящим от меня причинам не была завершена.Теги: #C++ #шаблоны c++Тема очень обширная, поэтому публикуется в усеченном виде.
Что не подошло, обсудим позже.
Невозможно понять современный C++, не зная, что такое шаблоны программирования.Это свойство языка открывает широкие возможности для оптимизации и повторного использования кода.
В этой статье мы попробуем разобраться, что это такое и как все это работает. Механизм шаблонов в языке C++ позволяет решить задачу унификации алгоритма для разных типов: нет необходимости писать разные функции для целочисленных, реальных или пользовательских типов — достаточно создать обобщенный алгоритм, не зависящий от тип данных, основанный только на общих свойствах.
Например, алгоритм сортировки может работать как с целыми числами, так и с объектами типа «автомобиль».
Существуют шаблоны функций и шаблоны классов.
Шаблоны функций — это общее описание поведения функций, которые можно вызывать на объектах разных типов.
Другими словами, шаблон функции (template function, generic function) — это семейство различных функций (или описание алгоритма).
По описанию шаблон функции похож на обычную функцию: отличие в том, что некоторые элементы не определены (типы, константы) и параметризованы.
Шаблоны классов -- обобщенное описание определяемого пользователем типа, в котором атрибуты и операции типа могут быть параметризованы.
Это конструкции, из которых можно создавать реальные классы, подставляя вместо параметров определенные аргументы.
Давайте подробнее рассмотрим шаблоны функций.
Шаблоны функций
Как написать свою первую функцию шаблона?
Рассмотрим случай определения минимального элемента из двух.В случае целых и вещественных чисел вам придется написать 2 функции.
int _min(int a, int b){ if( a < b){ return a; } return b; } double _min(double a, double b){ if( a < b){ return a; } return b; }
Можно, конечно, реализовать только одну функцию, с реальными параметрами, но это будет вредно для понимания шаблонов.Что произойдет, если приложение скомпилируется? Обе реализации функции попадут в бинарный код приложения, даже если они не используются (впрочем, компиляторы сейчас очень умные, они могут вырезать неиспользуемый код).
А что, если вам нужно добавить функцию, определяющую минимум 2 строки (сложно представить без указания, что такое минимальная строка)?! В этом случае, если алгоритм является общим для типов, с которыми вам нужно работать, вы можете определить шаблон функции.
В целом принцип будет следующим:
Для функции min вы получаете следующее:
- берется реализация функции для некоторого типа;
- шаблон заголовка (или шаблон ) присваивается, что означает, что алгоритм использует некоторый абстрактный тип Type;
- в реализации функции имя типа заменяется на Type.
template<class Type> Type _min(Type a, Type b){ if( a < b){ return a; } return b; }
Самое интересное, что пока нет вызова функции min, при компиляции она не создается в двоичном коде (не создан экземпляр ).А если объявить группу вызовов функций с переменными разных типов, то для каждого компилятор создаст свою реализацию на основе шаблона.
Вызов шаблонной функции в целом эквивалентен вызову обычной функции.
В этом случае компилятор определит, какой тип использовать вместо Type, исходя из типа фактических параметров.
Но если подставленные параметры окажутся разных типов, то компилятор не сможет вывести (создать экземпляр шаблона) реализацию шаблона.
Итак, в следующем коде компилятор споткнется на третьем вызове, так как не может определить, чему равен Type (задумайтесь, почему?):
#include <iostream> template<class Type> Type _min(Type a, Type b) { if (a < b) { return a; } return b; } int main(int argc, char** argv) { std::cout << _min(1, 2) << std::endl; std::cout << _min(3.1, 1.2) << std::endl; std::cout << _min(5, 2.1) << std::endl; // oops! return 0; }
Эта проблема решается путем указания конкретного типа при вызове функции.
#include <iostream> template<class Type> Type _min(Type a, Type b) { if (a < b) { return a; } return b; } int main(int argc, char** argv) { std::cout << _min<double>(5, 2.1) << std::endl; return 0; }
Когда функция шаблона (не) будет работать?
В принципе, можно понять, что компилятор просто подставляет в шаблон нужный тип.Но всегда ли будет работать полученная функция? Очевидно нет. Любой алгоритм может быть определен независимо от типа данных, но он должен использовать свойства этих данных.
В случае шаблонной функции _min для этого необходимо определить оператор упорядочивания (оператор < operator).
Любой шаблон функции предполагает наличие определенных свойств параметризованного типа в зависимости от реализации (например, оператор копирования, оператор сравнения, наличие определенного метода и т.п.
).
В ожидаемом стандарте языка C++ за это будут отвечать они.
Перегрузка шаблона функции
Шаблоны функций также можно перегружать.Обычно эта перегрузка выполняется, когда
template<class Type> Type* _min(Type* a, Type* b){ if(*a < *b){ return a; } return b; }
Особые случаи
В некоторых случаях шаблон функции неэффективен или неверен для определенного типа.В этом случае вы можете специализироваться шаблон — то есть написать реализацию для данного типа.
Например, в случае строк вы можете захотеть, чтобы функция сравнивала только количество символов.
В случае специализации шаблона функции тип, для которого указывается шаблон, в параметре не указывается.
Ниже приведен пример указанной специализации.
template<> std::string _min(std::string a, std::string b){ if(a.size() < b.size()){ return a; } return b; }
Специализация шаблона для конкретных типов опять-таки делается из соображений экономии: если эта версия шаблона функции не используется в коде, то она не будет включена в двоичный код.Множественные и целочисленные параметры останутся на будущее.Естественным расширением являются шаблоны классов, основы генеративного программирования и структура стандартной библиотеки C++.
И много примеров!
-
Сказка Про Sd-Wan
19 Oct, 24 -
Корпоративный Семинар
19 Oct, 24 -
Принципы Сотрудничества
19 Oct, 24 -
Опера 9.62
19 Oct, 24 -
Рефакторинг Схем Баз Данных
19 Oct, 24