Параметры шаблонов и аргументы шаблонов
Параметры шаблонов
Каждый шаблон параметризуется одним или несколькими параметрами шаблона, указанными в списке параметров синтаксиса объявления шаблона:
template < список параметров > объявление |
Каждый параметр в списке параметров может быть:
- параметром шаблона нетипового типа;
- параметром шаблона типового типа;
- параметром шаблона шаблона.
Параметр шаблона нетипового типа
| тип имя (опционально) | (1) | |
тип имя (опционально) = значение по умолчанию | (2) | |
тип ... имя (опционально) | (3) | (с C++11) |
| заполнитель имя (опционально) | (4) | (с C++17) |
заполнитель имя (опционально) = значение по умолчанию | (5) | (с C++17) |
заполнитель ... имя (опционально) | (6) | (с C++17) |
auto (например, простой auto, auto * или auto &), заполнитель для вывода типа класса(с C++20), или decltype(auto).Параметр шаблона нетипового типа должен иметь структурный тип, который является одним из следующих типов (по желанию с квалификаторами cv, квалификаторы игнорируются):
- тип ссылки на lvalue (на объект или на функцию);
- целочисленный тип;
- тип указателя (на объект или на функцию);
- тип указателя на член (на член-объект или на член-функцию);
- тип перечисления;
| (с C++11) |
| (с C++20) |
Массивы и типы функций могут быть написаны в объявлении шаблона, но они автоматически заменяются указателем на объект и указателем на функцию соответственно.
Когда имя параметра шаблона нетипового типа используется в выражении в теле шаблона класса, оно является неизменяемым prvalue, если только его тип не был типом ссылки на lvalue, или если его тип — тип класса(с C++20).
Параметр шаблона в форме class Foo не является безымянным параметром шаблона нетипового типа типа Foo, даже если в противном случае class Foo является развёрнутым указанием типа и class Foo x; объявляет x как тип Foo.
| Тип параметра шаблона нетипового типа может быть выведен, если он включает тип-заполнитель ( 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 | (с C++17) |
| Идентификатор, который называет параметр шаблона нетипового типа типа класса 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) |
Параметр шаблона типового типа
| ключ параметра типа имя (опционально) | (1) | |
ключ параметра типа имя (опционально) = значение по умолчанию | (2) | |
ключ параметра типа ... имя (опционально) | (3) | (с C++11) |
| ограничение типа имя (опционально) | (4) | (с C++20) |
ограничение типа имя (опционально) = значение по умолчанию | (5) | (с C++20) |
ограничение типа ... имя (опционально) | (6) | (с C++20) |
| ключ параметра типа | - | либо typename или class. Нет разницы между этими ключевыми словами в объявлении параметра шаблона типового типа |
| ограничение типа | - | либо имя концепции, либо имя концепции, за которым следует список аргументов шаблона (в угловых скобках). В любом случае имя концепции может быть необязательно квалифицировано |
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;
В теле объявления шаблона имя параметра типа — это псевдоним типа, который соответствует типу, предоставленному при инстанцировании шаблона.
| Каждый ограниченный параметр
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) |
| ключ параметра типа | - | 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), либо заданы по умолчанию. Для шаблонов функций аргументы явно указаны, выведены из контекста или заданы по умолчанию.
Если аргумент может быть интерпретирован как идентификатор типа и как выражение, он всегда интерпретируется как идентификатор типа, даже если соответствующий параметр шаблона является нетиповым:
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++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)Исключения составляют нетиповые параметры шаблона типа ссылка или указатель и нестатические члены данных типа ссылка или указатель в нетиповом параметре шаблона типа класс и его подобъекты(с C++20), которые не могут ссылаться на/иметь адрес
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 | (с C++17) |
Аргументы типа шаблона
Аргумент шаблона для параметра шаблона типа должен быть идентификатором типа, который может называть неполный тип:
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) |
Эквивалентность аргументов шаблона
Эквивалентность аргументов шаблона используется для определения того, являются ли два идентификатора шаблона одинаковыми.
Два значения эквивалентны по аргументам шаблона, если они имеют одинаковый тип и
- они имеют целочисленный или перечислительный тип, и их значения одинаковы
- или они имеют указательный тип и имеют одинаковое значение указателя
- или они имеют тип указателя на член и ссылаются на один и тот же член класса или оба являются нулевым указателем на член
- или они имеют тип ссылки на левостороннее значение и ссылаются на один и тот же объект или функцию
| (с C++11) |
| (с C++20) |
Примечания
| В параметрах шаблона ограничения типов могут использоваться как для параметров типов, так и для параметров нетипа, в зависимости от наличия 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) | Типы классов и типы с плавающей запятой в параметрах шаблона нетипа |
Примеры
#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 | C++98 | аргументы шаблонов-шаблонов должны были точно соответствовать спискам параметров шаблонов-шаблонов | также разрешено более специализированное соответствие (разрешено P0522R0) |
| 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 | для шаблонов переменных не было ограничений на параметры шаблона после параметра шаблона со значением по умолчанию | применить те же ограничения, что и для шаблонов классов и шаблонов псевдонимов |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/template_parameters