Template arguments
Для того чтобы шаблон был инстанцирован, каждый template parameter должен быть заменён соответствующим template argument. Аргументы могут быть явно указаны, выведены или установлены по умолчанию.
Каждый параметр в template-parameter-list (см. синтаксис шаблонных идентификаторов ) принадлежит к одной из следующих категорий:
- константный шаблонный аргумент
- тип в качестве шаблонного аргумента
- шаблон в качестве шаблонного аргумента
Содержание |
Константные аргументы шаблона
Также известны как non-type template arguments (см. ниже ).
|
Аргумент шаблона, который может использоваться с параметром шаблона-константой, может быть любым явно константно вычисляемым выражением . |
(до C++11) |
|
Аргумент шаблона, который может использоваться с параметром шаблона-константой, может быть любым инициализирующим выражением . Если инициализирующее выражение является выражением, оно должно быть явно константно вычисляемым . |
(начиная с C++11) |
Учитывая
тип
объявления
постоянного шаблонного параметра
как
T
и предоставленный для параметра шаблонный аргумент как
E
.
|
Изобретенное объявление T x = E ; должно удовлетворять семантическим ограничениям для определения constexpr переменной с статической продолжительностью хранения . |
(since C++20) |
|
Если
Если выведенный тип параметра не является структурным типом , программа является некорректной. Для параметров шаблона-паков констант, тип которых использует placeholder type, тип выводится независимо для каждого аргумента шаблона и не обязан совпадать. |
(since C++17) |
template<auto n> struct B { /* ... */ }; B<5> b1; // OK: тип параметра шаблона - int B<'a'> b2; // OK: тип параметра шаблона - char B<2.5> b3; // ошибка (до C++20): тип параметра шаблона не может быть double // C++20 placeholder выведенного типа класса, аргументы шаблона класса выводятся в // точке вызова template<std::array arr> void f(); f<std::array<double, 8>{}>(); template<auto...> struct C {}; C<'C', 0, 2L, nullptr> x; // OK
Значение параметра шаблона-константы
P
типа
(возможно, выведенного)
(since C++17)
T
определяется из его шаблонного аргумента
A
следующим образом:
|
(до C++11) |
|
(начиная с C++11)
(до C++20) |
|
(начиная с C++20) |
template<int i> struct C { /* ... */ }; C<{42}> c1; // OK template<auto n> struct B { /* ... */ }; struct J1 { J1* self = this; }; B<J1{}> j1; // ошибка: инициализация объекта параметра шаблона // не является константным выражением struct J2 { J2 *self = this; constexpr J2() {} constexpr J2(const J2&) {} }; B<J2{}> j2; // ошибка: объект параметра шаблона не является // эквивалентным по аргументу шаблона введенному временному объекту
|
Следующие ограничения применяются при инстанцировании шаблонов, имеющих константные параметры шаблона:
В частности, это означает, что строковые литералы, адреса элементов массивов и адреса нестатических членов не могут использоваться в качестве аргументов шаблона для инстанцирования шаблонов, чьи соответствующие константные параметры шаблона являются указателями на объекты. |
(until C++17) |
|
константные параметры шаблона ссылочного или указательного типа и нестатические члены данных ссылочного или указательного типа в константном параметре шаблона классового типа и его подобъектах (since C++20) не могут ссылаться на/быть адресом
|
(since C++17) |
template<const int* pci> struct X {}; int ai[10]; X<ai> xi; // OK: преобразование массива в указатель и преобразование cv-квалификации struct Y {}; template<const Y& b> struct Z {}; Y y; Z<y> z; // OK: без преобразования template<int (&pa)[5]> struct W {}; int b[5]; W<b> w; // OK: без преобразования void f(char); void f(int); template<void (* pf)(int)> struct A {}; A<&f> a; // OK: разрешение перегрузки выбирает f(int)
template<class T, const char* p> class X {}; X<int, "Studebaker"> x1; // ошибка: строковый литерал в качестве аргумента шаблона template<int* p> class X {}; int a[10]; struct S { int m; static int s; } s; X<&a[2]> x3; // ошибка (до C++20): адрес элемента массива X<&s.m> x4; // ошибка (до C++20): адрес нестатического члена X<&s.s> x5; // OK: адрес статического члена X<&S::s> x6; // OK: адрес статического члена template<const int& CRI> struct B {}; B<1> b2; // ошибка: для аргумента шаблона потребуется временный объект int c = 1; B<c> b1; // OK
Аргументы шаблона типа
Аргумент шаблона для параметра шаблона типа должен быть type-id , который может указывать на неполный тип:
template<typename T> class X {}; // шаблон класса struct A; // неполный тип typedef struct {} B; // псевдоним типа для безымянного типа int main() { X<A> x1; // OK: 'A' обозначает тип X<A*> x2; // OK: 'A*' обозначает тип X<B> x3; // OK: 'B' обозначает тип }
Шаблонные аргументы шаблонов
Аргумент шаблона для параметра шаблона-шаблона должен быть id-выражением , которое указывает на шаблон класса или псевдоним шаблона.
Когда аргументом является шаблон класса, при сопоставлении параметра рассматривается только основной шаблон. Частичные специализации, если они есть, рассматриваются только тогда, когда специализация на основе этого шаблонного параметра-шаблона инстанцируется.
template<typename T> // основной шаблон class A { int x; }; template<typename T> // частичная специализация class A<T*> { long x; }; // шаблон класса с параметром шаблона-шаблона V template<template<typename> class V> class C { V<int> y; // использует основной шаблон V<int*> z; // использует частичную специализацию }; C<A> c; // c.y.x имеет тип int, c.z.x имеет тип long
Чтобы соответствовать аргументу шаблона-шаблона
A
параметру шаблона-шаблона
P
,
P
должен быть
как минимум настолько же специализирован
, как и
A
(см. ниже).
Если список параметров
P
включает
пачку параметров
, ноль или более шаблонных параметров (или пачек параметров) из списка шаблонных параметров
A
сопоставляются с ней.
(начиная с C++11)
Формально, параметр шаблона-шаблона
P
является
как минимум столь же специализированным
, как аргумент шаблона-шаблона
A
, если при следующем преобразовании в два шаблона функций, шаблон функции, соответствующий
P
, является как минимум столь же специализированным, как шаблон функции, соответствующий
A
, согласно правилам частичного упорядочивания для
шаблонов функций
. Для введенного шаблона класса
X
со списком параметров шаблона
A
(включая аргументы по умолчанию):
-
Каждый из двух шаблонов функций имеет те же параметры шаблона, соответственно, что и
PилиA. -
Каждый шаблон функции имеет единственный параметр функции, тип которого является специализацией
Xс аргументами шаблона, соответствующими параметрам шаблона из соответствующего шаблона функции, где для каждого параметра шаблонаPPв списке параметров шаблона шаблона функции формируется соответствующий аргумент шаблонаAA. ЕслиPPобъявляет пачку параметров, тоAAявляется развёртыванием пачкиPP...; иначе, (since C++11)AAявляется id-выражениемPP.
Если в результате перезаписи получается недопустимый тип, то
P
не является как минимум настолько же специализированным, как
A
.
template<typename T> struct eval; // основной шаблон template<template<typename, typename...> class TT, typename T1, typename... Rest> struct eval<TT<T1, Rest...>> {}; // частичная специализация eval template<typename T1> struct A; template<typename T1, typename T2> struct B; template<int N> struct C; template<typename T1, int N> struct D; template<typename T1, typename T2, int N = 17> struct E; eval<A<int>> eA; // OK: соответствует частичной специализации eval eval<B<int, float>> eB; // OK: соответствует частичной специализации eval eval<C<17>> eC; // ошибка: C не соответствует TT в частичной специализации // потому что первый параметр TT является // параметром шаблона типа, а 17 не является типом eval<D<int, 17>> eD; // ошибка: D не соответствует TT в частичной специализации // потому что второй параметр TT является // пакетом параметров типа, а 17 не является типом eval<E<int, float>> eE; // ошибка: E не соответствует TT в частичной специализации // потому что третий (по умолчанию) параметр E является константой
До принятия
P0522R0
каждый из шаблонных параметров
A
должен был точно соответствовать соответствующим шаблонным параметрам
P
. Это препятствовало принятию многих разумных шаблонных аргументов.
Хотя это было отмечено очень рано ( CWG#150 ), к моменту решения изменения были применены к рабочему документу C++17, и решение стало де-факто функциональностью C++17. Многие компиляторы отключают его по умолчанию:
- GCC отключает это во всех режимах языка до C++17 по умолчанию, включить можно только установкой флага компилятора в этих режимах.
- Clang отключает это во всех режимах языка по умолчанию, включить можно только установкой флага компилятора.
- Microsoft Visual Studio рассматривает это как обычную функцию C++17 и включает только в режимах языка C++17 и новее (т.е. нет поддержки в режиме C++14, который является режимом по умолчанию).
template<class T> class A { /* ... */ }; template<class T, class U = T> class B { /* ... */ }; template<class... Types> class C { /* ... */ }; template<template<class> class P> class X { /* ... */ }; X<A> xa; // OK X<B> xb; // OK после P0522R0 // Ошибка ранее: нет точного соответствия X<C> xc; // OK после P0522R0 // Ошибка ранее: нет точного соответствия template<template<class...> class Q> class Y { /* ... */ }; Y<A> ya; // OK Y<B> yb; // OK Y<C> yc; // OK template<auto n> class D { /* ... */ }; // примечание: C++17 template<template<int> class R> class Z { /* ... */ }; Z<D> zd; // OK после P0522R0: параметр шаблона // более специализирован, чем аргумент шаблона template<int> struct SI { /* ... */ }; template<template<auto> class> void FA(); // примечание: C++17 FA<SI>(); // Ошибка
Эквивалентность аргументов шаблона
Эквивалентность аргументов шаблона используется для определения того, являются ли два идентификатора шаблона одинаковыми.
Два значения являются эквивалентными-по-аргументам-шаблона если они имеют одинаковый тип и выполняется любое из следующих условий:
- Они имеют целочисленный или перечислимый тип, и их значения одинаковы.
- Они имеют тип указателя и имеют одинаковое значение указателя.
- Они имеют тип указателя на член класса и ссылаются на один и тот же член класса или оба являются нулевым значением указателя на член.
- Они имеют тип lvalue-ссылки и ссылаются на один и тот же объект или функцию.
|
(начиная с C++11) |
|
(since C++20) |
Разрешение неоднозначностей
Если аргумент шаблона может быть интерпретирован и как type-id , и как выражение, он всегда интерпретируется как type-id, даже если соответствующий параметр шаблона является константным:
template<class T> void f(); // #1 template<int I> void f(); // #2 void g() { f<int()>(); // "int()" является одновременно и типом, и выражением, // вызывает #1, так как интерпретируется как тип }
Примечания
До C++26 постоянные аргументы шаблона назывались нетиповыми аргументами шаблона в стандартной терминологии. Терминология была изменена документом P2841R6 / PR #7587 .
| Макрос тестирования возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_template_template_args
|
201611L
|
(C++17)
(DR) |
Сопоставление шаблонных шаблонных аргументов |
__cpp_nontype_template_args
|
201411L
|
(C++17) | Разрешение постоянного вычисления для всех постоянных шаблонных аргументов |
201911L
|
(C++20) | Типы классов и типы с плавающей точкой в постоянных шаблонных параметрах |
Пример
|
Этот раздел не завершён
Причина: отсутствует пример |
Отчеты о дефектах
Следующие отчеты об изменениях в поведении, являющиеся дефектными, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
|
CWG 150
( P0522R0 ) |
C++98 |
template-template аргументы должны были точно соответствовать
спискам параметров template-template параметров |
более специализированные
также разрешены |
| CWG 354 | C++98 | нулевые указатели не могли быть константными template аргументами | разрешено |
| CWG 1398 | C++11 |
константные template аргументы не могли иметь тип
std::nullptr_t
|
разрешено |
| CWG 1570 | C++98 | константные template аргументы могли обозначать адреса подобъектов | не разрешено |
| P2308R1 |
C++11
C++20 |
1. list-initialization не была разрешена для
константных template аргументов (C++11) 2. было неясно, как инициализируются константные template параметры классовых типов (C++20) |
1. разрешено
2. прояснено |