Spec-Zone.ru › C++

Параметры шаблона и аргументы шаблона

Параметры шаблона

Каждый шаблон параметризован одним или несколькими параметрами шаблона, указанными в списке-параметров синтаксиса объявления шаблона:

template < parameter-list > declaration (1)
template < parameter-list > requires constraint declaration (2) (начиная с C++20)

Каждый параметр в списке-параметров может быть:

  • параметром шаблона, не являющимся типом;
  • параметром шаблона типа;
  • параметром шаблона-шаблона.
В P2841 (параметры шаблона-шаблона для концептов и шаблонов переменных) термины «параметр/аргумент шаблона, не являющийся типом» были заменены на «константный параметр/аргумент шаблона» без изменения их значений. Из-за широкого использования термина «параметр шаблона, не являющийся типом» и его сокращения «NTTP» сохраняется текущая терминология.

Параметр шаблона, не являющийся типом

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

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

  • тип ссылки на lvalue (на объект или функцию);
  • целочисленный тип;
  • тип указателя (на объект или функцию);
  • тип указателя на член (на объект-член или функцию-член);
  • тип перечисления;
  • std::nullptr_t;
(начиная с C++11)
  • тип с плавающей точкой;
  • тип замыкания лямбда-выражения, лямбда-выражение которого не имеет захватов;
  • тип литерального класса, не являющийся типом замыкания, со следующими свойствами:
    • все базовые классы и нестатические члены-данные являются открытыми и не изменяемыми, и
    • типы всех базовых классов и нестатических членов-данных являются структурными типами или (возможно, многомерными) массивами таких типов;
(начиная с C++20)
  • std::meta::info.
(начиная с C++26)

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

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

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

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

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
}

Объекты параметров шаблона также могут создаваться функциями рефлексии, например std::meta::reflect_constant_array.

(начиная с C++26)
(начиная с 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) Пакет параметров шаблона типа.
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) Пакет ограниченных параметров шаблона типа.
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, псевдонимом для типа, указанного при инстанцировании шаблона.

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

  • если Q — это C (без списка аргументов),
    • если P не является пакетом параметров, E — это просто C<P>
    • в противном случае, если P является пакетом параметров, E — это свёртка-выражение (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 < 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)
1) Параметр шаблона-шаблона с необязательным именем.
2) Параметр шаблона-шаблона с необязательным именем и значением по умолчанию.
3) Пакет параметров шаблона-шаблона с необязательным именем.

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

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.

Вымышленное объявление T x = E; должно удовлетворять семантическим ограничениям, предъявляемым к определению переменной constexpr со статической длительностью хранения.

(начиная с C++26)

Если T содержит тип-заполнитель или является заполнителем для выводимого типа класса, тип параметра шаблона — это тип, выведенный для переменной x в вымышленном объявлении T x = E;.

Если выведенный тип параметра не является структурным типом, программа некорректна.

Для пакетов параметров шаблона, не являющихся типом, тип которых использует тип-заполнитель, тип выводится независимо для каждого аргумента шаблона и может различаться.

(начиная с 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 следующим образом:

  • Если A является преобразованным константным выражением типа T, значением P является A (после преобразования).
  • В противном случае программа некорректна.
(до C++11)
  • Если A является выражением:
    • Если A является преобразованным константным выражением типа T, значением P является A (после преобразования).
    • В противном случае программа некорректна.
  • В противном случае (A является списком инициализации в фигурных скобках) вводится временная переменная constexpr T v = A;. Значением P является значение v.
    • Время жизни v заканчивается сразу после её инициализации.
(начиная с C++11)
(до C++20)
  • Если T не является типом класса, а A является выражением:
    • Если A является преобразованным константным выражением типа T, значением P является A (после преобразования).
    • В противном случае программа некорректна.
  • В противном случае (T является типом класса или A является списком инициализации в фигурных скобках) вводится временная переменная constexpr T v = A;.
  • Если T является типом класса, существует объект параметра шаблона (который также обозначается P). P копирующе инициализируется из неуказанного инициализатора-кандидата, который эквивалентен аргументу шаблона v.
  • Время жизни v заканчивается сразу после его инициализации и P.
  • Если инициализация P удовлетворяет любому из следующих условий, программа некорректна:
    • Инициализация некорректна.
    • Полное выражение вымышленной последовательности «декларатор-инициализатор» для инициализации не было бы константным выражением при интерпретации в качестве выражения, явно вычисляемого как константное.
    • Инициализация привела бы к тому, что P не был бы эквивалентен аргументу шаблона v.
  • В противном случае значением P является значение v.
(начиная с 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

При инстанцировании шаблонов с параметрами шаблона, не являющимися типами, действуют следующие ограничения:

  • Для целочисленных и арифметических типов аргумент шаблона, заданный при инстанцировании, должен быть преобразованным константным выражением типа параметра шаблона (поэтому применяются некоторые неявные преобразования).
  • Для указателей на объекты аргументы шаблона должны обозначать адрес полного объекта со статической длительностью хранения и связностью (внутренней или внешней) либо константное выражение, вычисляемое в соответствующее значение нулевого указателя или std::nullptr_t(начиная с C++11).
  • Для указателей на функции допустимыми аргументами являются указатели на функции со связностью (или константные выражения, вычисляемые в значения нулевого указателя).
  • Для параметров — ссылок на lvalue аргумент, заданный при инстанцировании, не может быть временным объектом, безымянным lvalue или именованным lvalue без связности (иными словами, аргумент должен иметь связность).
  • Для указателей на члены аргумент должен быть указателем на член, заданным в виде &Class::Member, или константным выражением, вычисляемым в значение нулевого указателя или std::nullptr_t(начиная с C++11).

В частности, это означает, что строковые литералы, адреса элементов массивов и адреса нестатических членов нельзя использовать как аргументы шаблона для инстанцирования шаблонов, соответствующие параметры которых, не являющиеся типами, являются указателями на объекты.

(до C++17)

Параметры шаблона ссылочного или указательного типа, не являющиеся типами, а также нестатические члены-данные ссылочного или указательного типа в параметре шаблона класса, не являющемся типом, и его подобъекты(начиная с C++20) не могут ссылаться на следующий объект или быть его адресом:

  • временный объект (в том числе созданный при инициализации ссылки);
  • строковый литерал;
  • результат typeid;
  • предопределённая переменная __func__;
  • или подобъект (включая нестатический член класса, подобъект базового класса или элемент массива) одного из перечисленных выше объектов(начиная с 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-ссылки и ссылаются на один и тот же объект или функцию.
  • Они имеют тип std::nullptr_t.
(начиная с C++11)
  • Они имеют тип с плавающей точкой и их значения идентичны.
  • Они имеют тип массива (в этом случае массивы должны быть объектами-членами какого-либо класса или объединения)(до C++26), и соответствующие элементы эквивалентны как аргументы шаблона.
  • Они имеют тип объединения и либо у обоих нет активного члена, либо у них один и тот же активный член, причём их активные члены эквивалентны как аргументы шаблона.
  • Они имеют тип замыкания лямбда-выражения.
  • Они имеют тип класса, не являющегося объединением, и соответствующие прямые подобъекты и члены-ссылки эквивалентны как аргументы шаблона.
(начиная с C++20)
  • Они имеют тип std::meta::info, и их значения сравниваются как равные.
(начиная с 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API