Ограничения шаблонов (как функций, так и классов) позволяют ограничить набор возможных типов, которые будут применяться параметрами шаблонов. Добавляя ограничения к параметрам шаблона, решаются следующие задачи:
Из заголовка шаблона сразу видно, какие аргументы шаблона разрешены, а какие нет.
Шаблон создается только в том случае, если аргументы шаблона удовлетворяют всем ограничениям.
любое нарушение ограничений шаблона приводит к сообщению об ошибке, которое гораздо ближе к первопричине проблемы, а именно к попытке использовать шаблон с неверными аргументами.
Начиная со стандарта С++20 в язык был добавлен оператор requires, который позволяет установить для параметров шаблонов ограничения.
template <параметры> requires ограничения содержимое шаблона;
Ограничения представляют условные выражения, которые возвращают значение типа bool - если параметр типа удовлетворяет условию, то возвращается true. Каждое ограничение предписывает одно или несколько требований для одного или нескольких параметров шаблона.
Например, мы хотим определить функцию, которая может складывать числа:
#include <iostream> template <typename T> requires std::is_same<T, int>::value || std::is_same<T, double>::value T sum(T a, T b){ return a + b;} int main() { std::cout << sum(3, 4) << std::endl; std::cout << sum(12.5, 4.3) << std::endl; //std::cout << sum(5l, 7l) << std::endl; }
Здесь определен шаблон функции sum, который принимает значения типа T и возвращает их сумму также в виде значения типа T. Для параметра T после слова requires
установлено ограничение
std::is_same<T, int>::value || std::is_same<T, double>::value
Для определения ограничения применяется встроенная структура std::is_same
из стандартной библиотеки C++. Эта структура в свою очередь типизируется двумя
типами. Переменная value структуры возвращает true
, если оба типа одинаковы. То есть выражение std::is_same<T, int>::value
возвратит true
,
если T - это int. Аналонично устанавливаем еще одно ограничение к типу - std::is_same<T, double>::value
. И с помощью операции || объединяем два ограничения.
То есть T может представлять либо int, либо double.
Далее мы можем передавать в функцию sum() значения типов, которые удовлетворяют этим ограничениям:
std::cout << sum(3, 4) << std::endl; // передаем int std::cout << sum(12.5, 4.3) << std::endl; // передаем double
Значения других же типов мы передать не можем. Так, в примере выше закомментрирована строка, где в функцию sum() передаются значения типа long
:
//std::cout << sum(5l, 7l) << std::endl; // long работать не будет
Если мы ее раскомментируем, то компилятор не скомпилирует программу и отобразит нам ошибку, которая сообщит, что в функцию sum переданы значения некорректных типов.