Template parameters
Каждый шаблон параметризуется одним или несколькими параметрами шаблона.
Каждый параметр в template-parameter-list (см. синтаксис объявления шаблона ) принадлежит к одной из следующих категорий:
- постоянный параметр шаблона
- параметр шаблона типа
- параметр шаблона шаблона
Содержание |
Константный параметр шаблона
Также известен как non-type template parameter (см. ниже ).
| type name (необязательно) | (1) | ||||||||
type
name
(необязательно)
=
default
|
(2) | ||||||||
type
...
name
(необязательно)
|
(3) | (начиная с C++11) | |||||||
| type | - |
один из следующих типов:
|
||||
| name | - | имя параметра шаблона-константы | ||||
| default | - | аргумент шаблона по умолчанию |
Структурный тип
— это один из следующих типов (опционально cv-квалифицированный, квалификаторы игнорируются):
- тип lvalue-ссылки (на объект или функцию);
- целочисленный тип ;
- указательный тип (на объект или функцию);
- тип указателя на член класса (на член-объект или член-функцию);
- перечислимый тип ;
| (начиная с C++11) |
|
(начиная с C++20) |
Типы массивов и функций могут быть записаны в объявлении шаблона, но они автоматически заменяются на указатель на объект и указатель на функцию соответственно.
Когда имя параметра шаблона-константы используется в выражении внутри тела шаблона класса, это является неизменяемым prvalue , если только его тип не был типом lvalue-ссылки , или если его тип не является классом (начиная с C++20) .
Параметр шаблона вида
class
Foo
не является безымянным константным параметром шаблона типа
Foo
, даже если в других контекстах
class
Foo
является
уточненным спецификатором типа
и
class
Foo x
;
объявляет
x
как имеющий тип
Foo
.
|
Идентификатор, который именует константный параметр шаблона типа класса
struct A { friend bool operator==(const A&, const A&) = default; }; template<A a> void f() { &a; // OK const A& ra = a, & rb = a; // Both bound to the same template parameter object assert(&ra == &rb); // passes } |
(начиная с C++20) |
Параметр шаблона типа
| type-parameter-key name (необязательно) | (1) | ||||||||
type-parameter-key
name
(необязательно)
=
default
|
(2) | ||||||||
type-parameter-key
...
name
(необязательно)
|
(3) | (начиная с C++11) | |||||||
| type-constraint name (необязательно) | (4) | (начиная с C++20) | |||||||
type-constraint
name
(необязательно)
=
default
|
(5) | (начиная с C++20) | |||||||
type-constraint
...
name
(необязательно)
|
(6) | (начиная с C++20) | |||||||
| type-parameter-key | - |
либо
typename
или
class
. Нет разницы между этими ключевыми словами в объявлении параметра шаблона типа
|
| type-constraint | - | либо имя концепта либо имя концепта, за которым следует список аргументов шаблона (в угловых скобках). В любом случае имя концепта может быть дополнительно квалифицировано |
| name | - | имя параметра шаблона типа |
| default | - | аргумент шаблона по умолчанию |
template<class T> class My_vector { /* ... */ };
template<class T = void> struct My_op_functor { /* ... */ };
template<typename... Ts> class My_tuple { /* ... */ };
template<My_concept T> class My_constrained_vector { /* ... */ };
template<My_concept T = void> class My_constrained_op_functor { /* ... */ };
template<My_concept... Ts> class My_constrained_tuple { /* ... */ };
Имя параметра является необязательным:
// Объявления шаблонов, показанных выше: template<class> class My_vector; template<class = void> struct My_op_functor; template<typename...> class My_tuple;
В теле объявления шаблона имя параметра типа является typedef-именем, которое псевдонимизирует тип, предоставленный при инстанцировании шаблона.
|
Каждый ограниченный параметр
template<typename T> concept C1 = true; template<typename... Ts> // variadic concept concept C2 = true; template<typename T, typename U> concept C3 = true; template<C1 T> struct s1; // constraint-expression is C1<T> template<C1... T> struct s2; // constraint-expression is (C1<T> && ...) template<C2... T> struct s3; // constraint-expression is (C2<T> && ...) template<C3<int> T> struct s4; // constraint-expression is C3<T, int> template<C3<int>... T> struct s5; // constraint-expression is (C3<T, int> && ...) |
(начиная с C++20) |
Шаблонный параметр шаблона
template
<
список-параметров
>
ключ-тип-параметра
имя
(необязательно)
|
(1) | ||||||||
template
<
список-параметров
>
ключ-тип-параметра
имя
(необязательно)
=
значение-по-умолчанию
|
(2) | ||||||||
template
<
список-параметров
>
ключ-тип-параметра
...
имя
(необязательно)
|
(3) | (начиная с C++11) | |||||||
| type-parameter-key | - |
class
или
typename
(начиная с C++17)
|
В теле объявления шаблона имя этого параметра является именем шаблона (и требует аргументов для инстанцирования).
template<typename T> class my_array {}; // два параметра типа и один параметр шаблона шаблона: template<typename K, typename V, template<typename> typename C = my_array> class Map { C<K> key; C<V> value; };
Разрешение имен для параметров шаблонов
Имя параметра шаблона не допускается переобъявлять в пределах его области видимости (включая вложенные области). Параметру шаблона не разрешается иметь то же имя, что и имя шаблона.
template<class T, int N> class Y { int T; // ошибка: повторное объявление параметра шаблона void f() { char T; // ошибка: повторное объявление параметра шаблона } }; template<class X> class X; // ошибка: повторное объявление параметра шаблона
В определении члена шаблона класса, которое появляется вне определения шаблона класса, имя члена шаблона класса скрывает имя параметра шаблона любых охватывающих шаблонов классов, но не скрывает параметр шаблона члена, если член является шаблоном класса или функции.
template<class T> struct A { struct B {}; typedef void C; void f(); template<class U> void g(U); }; template<class B> void A<B>::f() { B b; // B из A, а не параметр шаблона } template<class B> template<class C> void A<B>::g(C) { B b; // B из A, а не параметр шаблона C c; // параметр шаблона C, а не C из A }
В определении члена шаблона класса, которое находится вне пространства имен, содержащего определение шаблона класса, имя параметра шаблона скрывает имя члена этого пространства имен.
namespace N { class C {}; template<class T> class B { void f(T); }; } template<class C> void N::B<C>::f(C) { C b; // C - это параметр шаблона, а не N::C }
В определении шаблона класса или в определении члена такого шаблона, которое находится вне определения шаблона, для каждого независимого базового класса, если имя базового класса или имя члена базового класса совпадает с именем параметра шаблона, имя базового класса или имя члена скрывает имя параметра шаблона.
struct A { struct B {}; int C; int Y; }; template<class B, class C> struct X : A { B b; // B из A C b; // ошибка: C из A не является именем типа };
Аргументы шаблона по умолчанию
Аргументы шаблона по умолчанию указываются в списках параметров после знака = . Значения по умолчанию могут быть заданы для любого вида параметров шаблона (тип, константа или шаблон) , но не для пачек параметров (since C++11) .
Если для параметра шаблона основного шаблона класса указано значение по умолчанию , основного шаблона переменной, (since C++14) или шаблона псевдонима, каждый последующий параметр шаблона должен иметь аргумент по умолчанию , за исключением того, что самый последний параметр может быть пакетом параметров шаблона (since C++11) . В шаблоне функции нет ограничений на параметры, следующие за параметром по умолчанию , и пакет параметров может следовать за другими параметрами типа только если они имеют значения по умолчанию или могут быть выведены из аргументов функции (since C++11) .
Параметры по умолчанию не допускаются
- во внеклассовом определении члена шаблона класса (они должны быть предоставлены в объявлении внутри тела класса). Обратите внимание, что шаблоны членов нешаблонных классов могут использовать параметры по умолчанию в своих внеклассовых определениях (см. GCC bug 53856 )
- в объявлениях friend шаблонов классов
|
(до C++11) |
|
В объявлении шаблона дружественной функции аргументы шаблона по умолчанию разрешены только в том случае, если объявление является определением, и никакие другие объявления этой функции не появляются в данной единице трансляции. |
(since C++11) |
Аргументы шаблона по умолчанию, которые появляются в объявлениях, объединяются аналогично аргументам функций по умолчанию:
template<typename T1, typename T2 = int> class A; template<typename T1 = int, typename T2> class A; // приведенное выше эквивалентно следующему: template<typename T1 = int, typename T2 = int> class A;
Но один и тот же параметр не может иметь аргументы по умолчанию, заданные дважды в одной области видимости:
template<typename T = int> class X; template<typename T = int> class X {}; // ошибка
При разборе аргумента по умолчанию для параметра шаблона-константы первый невложенный > воспринимается как конец списка параметров шаблона, а не как оператор «больше»:
template<int i = 3 > 4> // синтаксическая ошибка class X { /* ... */ }; template<int i = (3 > 4)> // OK class Y { /* ... */ };
Списки параметров шаблонов для шаблонных параметров-шаблонов могут иметь собственные аргументы по умолчанию, которые действуют только там, где сам шаблонный параметр-шаблон находится в области видимости:
// шаблон класса с параметром типа, имеющим значение по умолчанию template<typename T = float> struct B {}; // шаблонный параметр шаблона T имеет список параметров, который // состоит из одного параметра типа со значением по умолчанию template<template<typename = float> typename T> struct A { void f(); void g(); }; // внешние определения функций-членов шаблона template<template<typename TT> class T> void A<T>::f() { T<> t; // ошибка: TT не имеет значения по умолчанию в области видимости } template<template<typename TT = char> class T> void A<T>::g() { T<> t; // OK: t имеет тип T<char> }
Доступ к членам для имён, используемых в параметре шаблона по умолчанию, проверяется при объявлении, а не в точке использования:
class B {}; template<typename T> class C { protected: typedef T TT; }; template<typename U, typename V = typename U::TT> class D: public U {}; D<C<B>>* d; // ошибка: C::TT является защищенным
|
Аргумент шаблона по умолчанию неявно инстанцируется, когда требуется значение этого аргумента по умолчанию, за исключением случаев, когда шаблон используется для именования функции: template<typename T, typename U = int> struct S {}; S<bool>* p; // The default argument for U is instantiated at this point // the type of p is S<bool, int>* |
(since C++14) |
Примечания
До C++26 параметры шаблона-константы в стандартной терминологии назывались параметрами шаблона, не являющимися типами. Терминология была изменена документами P2841R6 / PR#7587 .
|
В параметрах шаблонов ограничения типов могут использоваться как для типовых, так и для константных параметров, в зависимости от наличия auto . template<typename> concept C = true; template<C, // type parameter C auto // constant parameter > struct S{}; S<int, 0> s;
|
(since C++20) |
| Макрос тестирования возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_nontype_template_parameter_auto
|
201606L
|
(C++17) | Объявление постоянных параметров шаблона с помощью auto |
__cpp_nontype_template_args
|
201411L
|
(C++17) | Разрешение постоянного вычисления для всех постоянных аргументов шаблона |
201911L
|
(C++20) | Типы классов и типы с плавающей запятой в постоянных параметрах шаблона |
Примеры
#include <array> #include <iostream> #include <numeric> // простой шаблонный параметр-константа template<int N> struct S { int a[N]; }; template<const char*> struct S2 {}; // сложный пример с константами template < char c, // целочисленный тип int (&ra)[5], // ссылка на lvalue объекта (тип массива) int (*pf)(int), // указатель на функцию int (S<10>::*a)[10] // указатель на член-объект (тип int[10]) > struct Complicated { // вызывает функцию, выбранную во время компиляции // и сохраняет результат в массиве, выбранном во время компиляции void foo(char base) { ra[4] = pf(c - base); } }; // S2<"fail"> s2; // ошибка: строковый литерал нельзя использовать char okay[] = "okay"; // статический объект с линковкой // S2<&okay[0]> s3; // ошибка: элемент массива не имеет линковки S2<okay> s4; // работает int a[5]; int f(int n) { return n; } // C++20: NTTP может быть литеральным классом template<std::array arr> constexpr auto sum() { return std::accumulate(arr.cbegin(), arr.cend(), 0); } // C++20: аргументы шаблона класса выводятся в месте вызова static_assert(sum<std::array<double, 8>{3, 1, 4, 1, 5, 9, 2, 6}>() == 31.0); // C++20: вывод аргументов NTTP и CTAD static_assert(sum<std::array{2, 7, 1, 8, 2, 8}>() == 28); int main() { S<10> s; // s.a - это массив из 10 int s.a[9] = 4; Complicated<'2', a, f, &S<10>::a> c; c.foo('0'); std::cout << s.a[9] << a[4] << '\n'; }
Вывод:
42
|
Этот раздел не завершён
Причина: больше примеров |
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 184 | C++98 |
разрешено ли параметрам шаблонов шаблонных
параметров иметь аргументы по умолчанию - не указано |
спецификация добавлена |
| CWG 1922 | C++98 |
было неясно, может ли шаблон класса, имя которого является
injected-class-name, использовать аргументы по умолчанию из предыдущих объявлений |
разрешено |
| CWG 2032 | C++14 |
для шаблонов переменных не было ограничений на параметры шаблона
после параметра шаблона с аргументом по умолчанию |
применить те же ограничения
что и для шаблонов классов и псевдонимов шаблонов |
| CWG 2542 | C++20 | было неясно, является ли тип замыкания структурным | он не является структурным |
| CWG 2845 | C++20 | тип замыкания не был структурным |
он является структурным
если не захватывает переменные |