Namespaces
Variants

Template parameters

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Каждый шаблон параметризуется одним или несколькими параметрами шаблона.

Каждый параметр в template-parameter-list (см. синтаксис объявления шаблона ) принадлежит к одной из следующих категорий:

  • постоянный параметр шаблона
  • параметр шаблона типа
  • параметр шаблона шаблона

Содержание

Константный параметр шаблона

Также известен как non-type template parameter (см. ниже ).

type name  (необязательно) (1)
type name  (необязательно) = default (2)
type ... name  (необязательно) (3) (начиная с C++11)
type - один из следующих типов:
  • структурный тип (см. ниже)
(since C++17)
(since C++20)
name - имя параметра шаблона-константы
default - аргумент шаблона по умолчанию
1) Постоянный параметр шаблона.
2) Постоянный параметр шаблона с аргументом шаблона по умолчанию.
3) Постоянный шаблонный parameter pack .


Структурный тип — это один из следующих типов (опционально cv-квалифицированный, квалификаторы игнорируются):

  • тип lvalue-ссылки (на объект или функцию);
  • целочисленный тип ;
  • указательный тип (на объект или функцию);
  • тип указателя на член класса (на член-объект или член-функцию);
  • перечислимый тип ;
(начиная с C++11)
  • тип с плавающей запятой floating-point type ;
  • тип замыкания лямбда-выражения lambda closure type , лямбда-выражение которого не имеет захвата;
  • не-замыкающий literal class type со следующими свойствами:
  • все базовые классы и нестатические члены данных являются публичными и неизменяемыми, и
  • типы всех базовых классов и нестатических членов данных являются структурными типами или (возможно, многомерными) массивами таковых.
(начиная с C++20)

Типы массивов и функций могут быть записаны в объявлении шаблона, но они автоматически заменяются на указатель на объект и указатель на функцию соответственно.

Когда имя параметра шаблона-константы используется в выражении внутри тела шаблона класса, это является неизменяемым prvalue , если только его тип не был типом lvalue-ссылки , или если его тип не является классом (начиная с C++20) .

Параметр шаблона вида class Foo не является безымянным константным параметром шаблона типа Foo , даже если в других контекстах class Foo является уточненным спецификатором типа и class Foo x ; объявляет x как имеющий тип Foo .

Идентификатор, который именует константный параметр шаблона типа класса T , обозначает объект статической продолжительности хранения типа const T , называемый объектом параметра шаблона , который эквивалентен по аргументу шаблона соответствующему аргументу шаблона после его преобразования к типу параметра шаблона. Никакие два объекта параметров шаблона не являются эквивалентными по аргументу шаблона.

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 - аргумент шаблона по умолчанию
1) Параметр шаблона типа без значения по умолчанию.
template<class T>
class My_vector { /* ... */ };
2) Параметр шаблона типа со значением по умолчанию.
template<class T = void>
struct My_op_functor { /* ... */ };
3) Шаблонный параметр-пакет типа parameter pack .
template<typename... Ts>
class My_tuple { /* ... */ };
4) Ограниченный шаблонный параметр типа без значения по умолчанию.
template<My_concept T>
class My_constrained_vector { /* ... */ };
5) Шаблонный параметр типа с ограничением и значением по умолчанию.
template<My_concept T = void>
class My_constrained_op_functor { /* ... */ };
6) Шаблонный параметр с ограничением parameter pack .
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-именем, которое псевдонимизирует тип, предоставленный при инстанцировании шаблона.

Каждый ограниченный параметр P , чей type-constraint является Q, обозначающим концепт C , вводит constraint-expression E в соответствии со следующими правилами:

  • если Q является C (без списка аргументов),
  • если P не является parameter pack, E просто C<P>
  • иначе, P является parameter pack, E является fold-expression (C<P> && ...)
  • если Q является C<A1,A2...,AN> , тогда E является C<P,A1,A2,...AN> или (C<P,A1,A2,...AN> && ...) , соответственно.
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)
1) Шаблонный параметр-шаблон с необязательным именем.
2) Параметр шаблона шаблона с необязательным именем и значением по умолчанию.
3) Шаблонный шаблонный parameter pack с опциональным именем.


В теле объявления шаблона имя этого параметра является именем шаблона (и требует аргументов для инстанцирования).

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) .

Параметры по умолчанию не допускаются

(до 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 тип замыкания не был структурным он является структурным
если не захватывает переменные