Параметры шаблона и аргументы шаблона
Параметры шаблона
Каждый шаблон параметризован одним или несколькими параметрами шаблона, указанными в списке-параметров синтаксиса объявления шаблона:
template < parameter-list > declaration | (1) | |
template < parameter-list > requires constraint declaration | (2) | (начиная с C++20) |
Каждый параметр в списке-параметров может быть:
- параметром шаблона, не являющимся типом;
- параметром шаблона типа;
- параметром шаблона-шаблона.
Параметр шаблона, не являющийся типом
| type name (необязательно) | (1) | |
type name (необязательно) = default | (2) | |
type ... name (необязательно) | (3) | (начиная с C++11) |
| type | - | один из следующих типов:
|
||||
| name | - | имя параметра шаблона, не являющегося типом | ||||
| default | - | аргумент шаблона по умолчанию |
Структурный тип — это один из следующих типов (возможно, cv-квалифицированный; квалификаторы игнорируются):
- тип ссылки на lvalue (на объект или функцию);
- целочисленный тип;
- тип указателя (на объект или функцию);
- тип указателя на член (на объект-член или функцию-член);
- тип перечисления;
| (начиная с C++11) | |
| (начиная с C++20) |
| (начиная с C++26) |
Типы массивов и функций можно указывать в объявлении шаблона, однако они автоматически заменяются соответственно на указатель на объект и указатель на функцию.
Когда имя параметра шаблона, не являющегося типом, используется в выражении внутри тела шаблона класса, оно является неизменяемым 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 { /* ... */ };
Имя параметра необязательно:
// Declarations of the templates shown above: 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 < parameter-list > type-parameter-key name (необязательно) | (1) | |
template < parameter-list > type-parameter-key name (необязательно) = default | (2) | |
template < parameter-list > type-parameter-key ... name (необязательно) | (3) | (начиная с C++11) |
| type-parameter-key | - |
class или typename(начиная с C++17) |
В теле объявления шаблона имя этого параметра является именем шаблона (для инстанцирования ему нужны аргументы).
template<typename T>
class my_array {};
// two type template parameters and one template template parameter:
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; // error: template parameter redeclared
void f()
{
char T; // error: template parameter redeclared
}
};
template<class X>
class X; // error: template parameter redeclared
В определении члена шаблона класса, расположенном вне определения шаблона класса, имя члена шаблона класса скрывает имя параметра шаблона любого охватывающего шаблона класса, но не имя параметра шаблона этого члена, если член является шаблоном класса или функции.
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; // A's B, not the template parameter
}
template<class B>
template<class C>
void A<B>::g(C)
{
B b; // A's B, not the template parameter
C c; // the template parameter C, not A's C
}
В определении члена шаблона класса, расположенном вне пространства имён, содержащего определение шаблона класса, имя параметра шаблона скрывает имя члена этого пространства имён.
namespace N
{
class C {};
template<class T>
class B
{
void f(T);
};
}
template<class C>
void N::B<C>::f(C)
{
C b; // C is the template parameter, not N::C
}
В определении шаблона класса или в определении члена такого шаблона, расположенном вне определения шаблона, для каждого независимого базового класса, если имя базового класса или имя члена базового класса совпадает с именем параметра шаблона, имя базового класса или члена скрывает имя параметра шаблона.
struct A
{
struct B {};
int C;
int Y;
};
template<class B, class C>
struct X : A
{
B b; // A's B
C b; // error: A's C isn't a type name
};
Аргументы шаблона
Чтобы шаблон можно было инстанцировать, каждый параметр шаблона (тип, нетиповой или шаблонный) необходимо заменить соответствующим аргументом шаблона. Для шаблонов классов аргументы либо задаются явно, выводятся из инициализатора, (начиная с C++17), либо берутся по умолчанию. Для шаблонов функций аргументы задаются явно, выводятся из контекста либо берутся по умолчанию.
Если аргумент можно интерпретировать и как type-id, и как выражение, он всегда интерпретируется как type-id, даже если соответствующий параметр шаблона не является типом:
template<class T>
void f(); // #1
template<int I>
void f(); // #2
void g()
{
f<int()>(); // "int()" is both a type and an expression,
// calls #1 because it is interpreted as a type
}
Аргументы шаблона, не являющиеся типами
| Аргументом шаблона, который можно использовать с параметром шаблона, не являющимся типом, может быть любое выражение, явно вычисляемое как константное. | (до C++11) |
| Аргументом шаблона, который можно использовать с параметром шаблона, не являющимся типом, может быть любой элемент списка инициализации. Если элемент списка инициализации является выражением, оно должно быть явно вычисляемым как константное. | (начиная с C++11) |
Пусть type в объявлении параметра шаблона, не являющегося типом, равен T, а аргумент шаблона, заданный для параметра, равен E.
| Вымышленное объявление | (начиная с C++26) |
| Если Если выведенный тип параметра не является структурным типом, программа некорректна. Для пакетов параметров шаблона, не являющихся типом, тип которых использует тип-заполнитель, тип выводится независимо для каждого аргумента шаблона и может различаться. | (начиная с C++17) |
template<auto n>
struct B { /* ... */ };
B<5> b1; // OK: non-type template parameter type is int
B<'a'> b2; // OK: non-type template parameter type is char
B<2.5> b3; // error (until C++20): non-type template parameter type cannot be double
// C++20 deduced class type placeholder, class template arguments are deduced at the
// call site
template<std::array arr>
void f();
f<std::array<double, 8>{}>();
template<auto...>
struct C {};
C<'C', 0, 2L, nullptr> x; // OK
Значение параметра шаблона, не являющегося типом, P типа T (возможно, выведенного(начиная с C++17)) определяется по его аргументу шаблона 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; // error: initialization of the template parameter object
// is not a constant expression
struct J2
{
J2 *self = this;
constexpr J2() {}
constexpr J2(const J2&) {}
};
B<J2{}> j2; // error: the template parameter object is not
// template-argument-equivalent to introduced temporary
| При инстанцировании шаблонов с параметрами шаблона, не являющимися типами, действуют следующие ограничения:
В частности, это означает, что строковые литералы, адреса элементов массивов и адреса нестатических членов нельзя использовать как аргументы шаблона для инстанцирования шаблонов, соответствующие параметры которых, не являющиеся типами, являются указателями на объекты. | (до C++17) |
| Параметры шаблона ссылочного или указательного типа, не являющиеся типами, а также нестатические члены-данные ссылочного или указательного типа в параметре шаблона класса, не являющемся типом, и его подобъекты(начиная с C++20) не могут ссылаться на следующий объект или быть его адресом:
| (начиная с C++17) |
template<const int* pci>
struct X {};
int ai[10];
X<ai> xi; // OK: array to pointer conversion and cv-qualification conversion
struct Y {};
template<const Y& b>
struct Z {};
Y y;
Z<y> z; // OK: no conversion
template<int (&pa)[5]>
struct W {};
int b[5];
W<b> w; // OK: no conversion
void f(char);
void f(int);
template<void (*pf)(int)>
struct A {};
A<&f> a; // OK: overload resolution selects f(int)
template<class T, const char* p>
class X {};
X<int, "Studebaker"> x1; // error: string literal as template-argument
template<int* p>
class X {};
int a[10];
struct S
{
int m;
static int s;
} s;
X<&a[2]> x3; // error (until C++20): address of array element
X<&s.m> x4; // error (until C++20): address of non-static member
X<&s.s> x5; // OK: address of static member
X<&S::s> x6; // OK: address of static member
template<const int& CRI>
struct B {};
B<1> b2; // error: temporary would be required for template argument
int c = 1;
B<c> b1; // OK
Аргументы шаблона типа
Аргументом шаблона для параметра шаблона типа должен быть type-id, который может обозначать неполный тип:
template<typename T>
class X {}; // class template
struct A; // incomplete type
typedef struct {} B; // type alias to an unnamed type
int main()
{
X<A> x1; // OK: 'A' names a type
X<A*> x2; // OK: 'A*' names a type
X<B> x3; // OK: 'B' names a type
}
Аргументы шаблона-шаблона
Аргументом шаблона для параметра шаблона-шаблона должно быть выражение-идентификатор, обозначающее шаблон класса или псевдоним шаблона.
Если аргументом является шаблон класса, при сопоставлении с параметром рассматривается только первичный шаблон. Частичные специализации, если они есть, рассматриваются только при инстанцировании специализации, основанной на этом параметре шаблона-шаблона.
template<typename T> // primary template
class A { int x; };
template<typename T> // partial specialization
class A<T*> { long x; };
// class template with a template template parameter V
template<template<typename> class V>
class C
{
V<int> y; // uses the primary template
V<int*> z; // uses the partial specialization
};
C<A> c; // c.y.x has type int, c.z.x has type long
Для сопоставления аргумента шаблона-шаблона A с параметром шаблона-шаблона P, P должен быть не менее специализированным, чем A (см. ниже). Если список параметров P включает пакет параметров, ему сопоставляется ноль или более параметров шаблона (или пакетов параметров) из списка параметров шаблона A.(начиная с C++11)
Формально параметр шаблона-шаблона P является не менее специализированным, чем аргумент шаблона-шаблона A, если после следующего преобразования в два шаблона функций шаблон функции, соответствующий P, не менее специализирован, чем шаблон функции, соответствующий A, согласно правилам частичного упорядочения шаблонов функций. Пусть имеется вымышленный шаблон класса X со списком параметров шаблона A (включая аргументы по умолчанию):
- Каждый из двух шаблонов функций имеет те же параметры шаблона, что и
PилиAсоответственно. - Каждый шаблон функции имеет один параметр функции, тип которого является специализацией
Xс аргументами шаблона, соответствующими параметрам шаблона соответствующего шаблона функции, где для каждого параметра шаблонаPPв списке параметров шаблона функции формируется соответствующий аргумент шаблонаAA. ЕслиPPобъявляет пакет параметров, тогдаAAявляется расширением пакетаPP...; в противном случае,(начиная с C++11)AAявляется выражением-идентификаторомPP.
Если преобразование приводит к некорректному типу, то P не является не менее специализированным, чем A.
template<typename T>
struct eval; // primary template
template<template<typename, typename...> class TT, typename T1, typename... Rest>
struct eval<TT<T1, Rest...>> {}; // partial specialization of 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: matches partial specialization of eval
eval<B<int, float>> eB; // OK: matches partial specialization of eval
eval<C<17>> eC; // error: C does not match TT in partial specialization
// because TT's first parameter is a
// type template parameter, while 17 does not name a type
eval<D<int, 17>> eD; // error: D does not match TT in partial specialization
// because TT's second parameter is a
// type parameter pack, while 17 does not name a type
eval<E<int, float>> eE; // error: E does not match TT in partial specialization
// because E's third (default) parameter is a non-type
До принятия 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 after P0522R0
// Error earlier: not an exact match
X<C> xc; // OK after P0522R0
// Error earlier: not an exact match
template<template<class...> class Q> class Y { /* ... */ };
Y<A> ya; // OK
Y<B> yb; // OK
Y<C> yc; // OK
template<auto n> class D { /* ... */ }; // note: C++17
template<template<int> class R> class Z { /* ... */ };
Z<D> zd; // OK after P0522R0: the template parameter
// is more specialized than the template argument
template<int> struct SI { /* ... */ };
template<template<auto> class> void FA(); // note: C++17
FA<SI>(); // Error
Аргументы шаблона по умолчанию
Аргументы шаблона по умолчанию указываются в списках параметров после знака =. Аргументы по умолчанию можно указывать для параметров шаблона любого вида (типа, не являющегося типом, или шаблона), но не для пакетов параметров(начиная с C++11).
Если аргумент по умолчанию указан для параметра шаблона первичного шаблона класса, первичного шаблона переменной(начиная с C++14) или шаблона псевдонима, каждый последующий параметр шаблона должен иметь аргумент по умолчанию, кроме самого последнего, который может быть пакетом параметров(начиная с C++11). В шаблоне функции ограничений на параметры, следующие за параметром со значением по умолчанию, нет, а за пакетом параметров могут следовать другие параметры типа, только если у них есть значения по умолчанию или их можно вывести из аргументов функции(начиная с C++11).
Параметры по умолчанию не допускаются
- в определении вне класса члена шаблона класса (их необходимо указывать в объявлении внутри тела класса). Обратите внимание, что шаблоны-члены классов, не являющихся шаблонами, могут использовать параметры по умолчанию в определениях вне класса (см. ошибку GCC 53856)
- в объявлениях шаблонов классов-друзей
| (до C++11) |
| В объявлении шаблона функции-друга аргументы шаблона по умолчанию допускаются только в том случае, если объявление является определением и в этой единице трансляции нет других объявлений этой функции. | (начиная с C++11) |
Аргументы шаблона по умолчанию, указанные в объявлениях, объединяются аналогично аргументам функции по умолчанию:
template<typename T1, typename T2 = int> class A; template<typename T1 = int, typename T2> class A; // the above is the same as the following: template<typename T1 = int, typename T2 = int> class A;
Однако одному и тому же параметру нельзя дважды задавать аргументы по умолчанию в одной области видимости:
template<typename T = int> class X;
template<typename T = int> class X {}; // error
При разборе аргумента шаблона по умолчанию для параметра шаблона, не являющегося типом, первый незаключённый во вложенные скобки > рассматривается как конец списка параметров шаблона, а не как оператор «больше»:
template<int i = 3 > 4> // syntax error
class X { /* ... */ };
template<int i = (3 > 4)> // OK
class Y { /* ... */ };
Списки параметров шаблонов параметров-шаблонов могут иметь собственные аргументы по умолчанию, действующие только в области видимости самого параметра-шаблона:
// class template, with a type template parameter with a default
template<typename T = float>
struct B {};
// template template parameter T has a parameter list, which
// consists of one type template parameter with a default
template<template<typename = float> typename T>
struct A
{
void f();
void g();
};
// out-of-body member function template definitions
template<template<typename TT> class T>
void A<T>::f()
{
T<> t; // error: TT has no default in scope
}
template<template<typename TT = char> class T>
void A<T>::g()
{
T<> t; // OK: t is 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; // error: C::TT is protected
| Аргумент шаблона по умолчанию неявно инстанцируется, когда требуется его значение, кроме случая, когда шаблон используется для обозначения функции: 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>*
| (начиная с C++14) |
Эквивалентность аргументов шаблона
Эквивалентность аргументов шаблона используется для определения того, являются ли два идентификатора шаблона одинаковыми.
Два значения эквивалентны как аргументы шаблона, если они имеют один и тот же тип и выполняется одно из следующих условий:
- Они имеют целочисленный тип или тип перечисления и их значения совпадают.
- Они имеют тип указателя и значения указателей совпадают.
- Они имеют тип указателя на член и ссылаются на один и тот же член класса либо оба являются нулевым указателем на член.
- Они имеют тип lvalue-ссылки и ссылаются на один и тот же объект или функцию.
| (начиная с C++11) |
| (начиная с C++20) |
| (начиная с C++26) |
Примечания
| В параметрах шаблона ограничения типов можно использовать как для параметров типа, так и для параметров, не являющихся типом, в зависимости от наличия auto. template<typename>
concept C = true;
template<C, // type parameter
C auto // non-type parameter
>
struct S{};
S<int, 0> s;
| (начиная с C++20) |
| Макрос проверки поддержки возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_nontype_template_parameter_auto | 201606L | (C++17) | Объявление параметров шаблона, не являющихся типами, с auto |
__cpp_template_template_args | 201611L |
(C++17) (DR) | Сопоставление аргументов-шаблонов |
__cpp_nontype_template_args | 201411L | (C++17) | Разрешение вычисления констант для всех аргументов шаблона, не являющихся типами |
201911L | (C++20) | Типы классов и типы с плавающей точкой в параметрах шаблона, не являющихся типами | |
__cpp_template_parameters | 202502L | (C++26) | Концепты и параметры-шаблоны шаблонов переменных |
Примеры
#include <array>
#include <iostream>
#include <numeric>
// simple non-type template parameter
template<int N>
struct S { int a[N]; };
template<const char*>
struct S2 {};
// complicated non-type example
template
<
char c, // integral type
int (&ra)[5], // lvalue reference to object (of array type)
int (*pf)(int), // pointer to function
int (S<10>::*a)[10] // pointer to member object (of type int[10])
>
struct Complicated
{
// calls the function selected at compile time
// and stores the result in the array selected at compile time
void foo(char base)
{
ra[4] = pf(c - base);
}
};
// S2<"fail"> s2; // error: string literal cannot be used
char okay[] = "okay"; // static object with linkage
// S2<&okay[0]> s3; // error: array element has no linkage
S2<okay> s4; // works
int a[5];
int f(int n) { return n; }
// C++20: NTTP can be a literal class type
template<std::array arr>
constexpr
auto sum() { return std::accumulate(arr.cbegin(), arr.cend(), 0); }
// C++20: class template arguments are deduced at the call site
static_assert(sum<std::array<double, 8>{3, 1, 4, 1, 5, 9, 2, 6}>() == 31.0);
// C++20: NTTP argument deduction and CTAD
static_assert(sum<std::array{2, 7, 1, 8, 2, 8}>() == 28);
int main()
{
S<10> s; // s.a is an array of 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 | Применён к | Опубликованное поведение | Корректное поведение |
|---|---|---|---|
|
CWG 150 (P0522R0) | C++98 | Аргументы-шаблоны должны были точно соответствовать спискам параметров параметров-шаблонов | Допускается также более специализированный вариант |
| CWG 184 | C++98 | Не было определено, могут ли параметры шаблонов параметров-шаблонов иметь аргументы по умолчанию | Добавлена спецификация |
| CWG 354 | C++98 | Нулевые значения указателей не могли быть аргументами шаблона, не являющимися типами | Разрешено |
| CWG 1398 | C++11 | Аргументы шаблона, не являющиеся типами, не могли иметь тип std::nullptr_t | Разрешено |
| CWG 1570 | C++98 | Аргументы шаблона, не являющиеся типами, могли обозначать адреса подобъектов | Не допускается |
| CWG 1922 | C++98 | Было неясно, может ли шаблон класса, имя которого является внедрённым именем класса, использовать аргументы по умолчанию из предыдущих объявлений | Разрешено |
| CWG 2032 | C++14 | Для шаблонов переменных не было ограничений на параметры шаблона, следующие за параметром шаблона со значением по умолчанию | Применяется то же ограничение, что и для шаблонов классов и шаблонов псевдонимов |
| CWG 2542 | C++20 | Было неясно, является ли тип замыкания структурным | Он не является структурным |
| CWG 2845 | C++20 | Тип замыкания не являлся структурным | Он является структурным, если лямбда-выражение не захватывает переменные |
| P2308R1 | C++11 C++20 | 1. Списочная инициализация не допускалась для аргументов шаблона, не являющихся типами (C++11) 2. Было неясно, как инициализируются параметры шаблона типов классов, не являющиеся типами (C++20) | 1. Разрешена 2. Разъяснено |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/template_parameters