Пространства имён
Варианты
Действия

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

Материал из cppreference.com
< cpp‎ | language
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений (до C++17*)
Спецификатор noexcept (C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype (C++11)
auto (C++11)
alignas (C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr (C++11)
Определяемые пользователем (C++11)
Утилиты
Атрибуты (C++11)
Types
Объявление typedef
Объявление псевдонима типа (C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Разное
 
 

Содержание

[править] Параметры шаблона

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

template < список-параметров > объявление

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

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

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

тип имя (необязательно) (1)
тип имя (необязательно) = default (2)
тип ... имя (необязательно) (3) (начиная с C++11)
заполнитель имя (4) (начиная с C++17)
1) Параметр шаблона не тип с необязательным именем.
2) Параметр шаблона не тип с необязательным именем и значением по умолчанию.
3) Пакет параметров не типов шаблона с необязательным именем.
4) Параметр шаблона не тип с типом-заполнителем. Заполнитель может быть любого типа, который включает заполнитель auto (например, обычный auto, auto * или auto &), заполнитель для выведенного типа класса или (начиная с C++20) или decltype(auto).

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

(начиная с C++11)
  • все базовые классы и нестатические элементы данных являются открытыми и не mutable и
  • типы всех базовых классов и нестатических элементов данных являются структурными типами или их (возможно, многомерными) массивами.
(начиная с C++20)

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

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

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

Тип параметра шаблона не типа может быть выведен, если он включает тип-заполнитель (auto, заполнитель для типа выведенного класса (начиная с C++20), или decltype(auto)). Вывод выполняется как бы выводом типа переменной x в придуманном объявлении T x = шаблон-аргумент;, где T объявленный тип параметра шаблона. Если выведенный тип не разрешён для параметра шаблона не типа, программа некорректна.

template<auto n>
struct B { /* ... */ };
 
B<5> b1;   // OK: тип параметра шаблона не типа есть int
B<'a'> b2; // OK: тип параметра шаблона не типа есть char
B<2.5> b3; // ошибка (до С++20): тип параметра шаблона не типа не может быть double
 
// С++20 выводит заполнитель классового типа, аргументы шаблона класса
// выводятся в месте вызова
template<std::array arr>
void f();
 
f<std::array<double, 8>{}>();

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

template<auto...>
struct C {};
 
C<'C', 0, 2L, nullptr> x; // OK
(начиная с C++17)

Идентификатор, который именует параметр шаблона не типа типа класса 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; // Оба связаны с одним и тем же объектом параметра шаблона
    assert(&ra == &rb);       // проходит
}
(начиная с C++20)

[править] Параметр шаблона тип

ключ-параметра-типа имя (необязательно) (1)
ключ-параметра-типа имя (необязательно) = default (2)
ключ-параметра-типа ... имя (необязательно) (3) (начиная с C++11)
ограничение-типа имя (необязательно) (4) (начиная с C++20)
ограничение-типа имя (необязательно) = default (5) (начиная с C++20)
ограничение-типа ... имя (необязательно) (6) (начиная с C++20)
ключ-параметра-типа либо typename, либо class. Между этими ключевыми словами в объявлении параметра шаблона типа нет разницы
ограничение-типа либо имя концепта, либо имя концепта, за которым следует список аргументов шаблона (в угловых скобках). В любом случае, имя концепта может быть дополнительно уточнено
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 { /* ... */ };

Имя параметра необязательно:

// Объявления шаблонов, показанных выше:
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> // вариативный концепт
concept C2 = true;
template<typename T, typename U>
concept C3 = true;
 
template<C1 T>         struct s1; // выражение-ограничения C1<T>
template<C1... T>      struct s2; // выражение-ограничения (C1<T> && ...)
template<C2... T>      struct s3; // выражение-ограничения (C2<T> && ...)
template<C3<int> T>    struct s4; // выражение-ограничения C3<T, int>
template<C3<int>... T> struct s5; // выражение-ограничения (C3<T, int> && ...)
(начиная с C++20)

[править] Параметр шаблона шаблона

template < список-параметров > ключ-параметра-типа имя (необязательно) (1)
template < список-параметров > ключ-параметра-типа имя (необязательно) = default (2)
template < список-параметров > ключ-параметра-типа ... имя (необязательно) (3) (начиная с C++11)
ключ-параметра-типа class или typename (начиная с C++17)
1) Параметр шаблона шаблона с необязательным именем.
2) Параметр шаблона шаблона с необязательным именем и значением по умолчанию.
3) Пакет параметров шаблона шаблона с необязательным именем.

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

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 это не имя типа
};

[править] Аргументы шаблона

Для создания экземпляра шаблона каждый параметр шаблона (тип, не тип или шаблон) должен быть заменён соответствующим аргументом шаблона. Для шаблонов классов аргументы либо предоставляются явно , выведенными из инициализатора, (начиная с C++17) либо по умолчанию. Для шаблонов функций аргументы указываются явно, выводятся из контекста, или устанавливаются по умолчанию.

Если аргумент может быть интерпретирован как идентификатор типа, так и как выражение, он всегда интерпретируется как идентификатор типа, даже если соответствующий параметр шаблона не является типом:

template<class T>
void f(); // #1
 
template<int I>
void f(); // #2
 
void g()
{
    f<int()>(); // "int()" это и тип, и выражение,
                // вызывает #1, потому что оно интерпретируется как тип
}

[править] Аргументы шаблона не типы

Следующие ограничения применяются при создании экземпляров шаблонов с параметрами шаблона, отличными от типа:

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

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

(до C++17)

Аргумент шаблона, который можно использовать с параметром шаблона не типом, может ��ыть любым преобразованным константным выражением типа параметра шаблона.

template<const int* pci>
struct X {};
 
int ai[10];
X<ai> xi; // OK: преобразование массива в указатель и cv-квалификационное преобразование
 
struct Y {};
 
template<const Y& b>
struct Z {};
 
Y y;
Z<y> z;   // OK: нет преобразования
 
template<int (&pa)[5]>
struct W {};
 
int b[5];
W<b> w;   // OK: нет преобразования
 
void f(char);
void f(int);
 
template<void (*pf)(int)>
struct A {};
 
A<&f> a;  // OK: разрешение перегрузки выбирает f(int)

Единственными исключениями являются параметры шаблона не типы типа ссылки или указателя и нестатические элементы данных типа ссылки или указателя в параметре шаблона не типа классового типа и его подобъекты (начиная с C++20) не могут ссылаться на /быть адресом

  • временный объект (в том числе созданный во время инициализации ссылки);
  • строковый литерал;
  • результат typeid;
  • предопределённая переменная __func__;
  • или подобъект (включая нестатический элемент класса, базовый подобъект или элемент массива) одного из вышеперечисленных (начиная с C++20).
template<class T, const char* p>
class X {};
 
X<int, "Studebaker"> x1; // ошибка: строковый литерал как аргумент шаблона
 
template<int* p>
class X {};
 
int a[10];
 
struct S
{
    int m;
    static int s;
} s;
 
X<&a[2]> x3; // ошибка (до C++20): адрес элемента массива
X<&s.m> x4;  // ошибка (до C++20): адрес нестатического элемента
X<&s.s> x5;  // OK: адрес статического элемента
X<&S::s> x6; // OK: адрес статического элемента
 
template<const int& CRI>
struct B {};
 
B<1> b2;     // ошибка: для аргумента шаблона требуется временное значение
int c = 1;
B<c> b1;     // OK
(начиная с C++17)

[править] Аргументы шаблона типы

Аргумент шаблона для параметра шаблона типа должен быть идентификатором-типа, который может именовать неполный тип:

template<typename T>
class X {}; // шаблон класса
 
struct A;            // неполный тип
typedef struct {} B; // псевдоним типа для безымянного типа
 
int main()
{
    X<A> x1;  // OK: 'A' именует тип
    X<A*> x2; // OK: 'A*' именует тип
    X<B> x3;  // OK: 'B' именует тип
}

[править] Аргументы шаблона шаблона

Аргумент шаблона для параметра шаблона шаблона должен быть выражением-идентификатором, которое именует шаблон класса или псевдоним шаблона.

Если аргумент является шаблоном класса, при сопоставлении с параметром учитывается только основной шаблон. Частичные специализации, если таковые имеются, учитываются только тогда, когда специализация, основанная на этом параметре шаблона шаблона, создаётся.

template<typename T> // основной шаблон
class A { int x; };
 
template<typename T> // частичная специализация
class A<T*> { long x; };
 
// шаблон класса с параметром шаблона шаблона V
template<template<typename> class V>
class C
{
    V<int> y;  // использует основной шаблон
    V<int*> z; // использует частичную специализацию
};
 
C<A> c; // c.y.x имеет тип int, c.z.x имеет тип long

Чтобы сопоставить аргумент шаблона шаблона A с параметром шаблона шаблона P, P должен быть по крайней мере так же специализированным, как A (смотрите ниже). Если список параметров P включает пакет параметров, ноль или более параметров шаблона (или пакетов параметров) из списка параметров шаблона A совпадают с ним. (начиная с C++11)

Формально параметр шаблона шаблона P является по крайней мере так же специализированным, как и аргумент шаблона шаблона A, если при следующей перезаписи двух шаблонов функций шаблон функции, соответствующий P, не менее специализирован, чем шаблон функции, соответствующий A, в соответствии с правилами частичного упорядочивания для шаблонов функций. Учитывая придуманный шаблон класса X со списком параметров шаблона A (включая аргументы по умолчанию):

  • Каждый из двух шаблонов функций имеет одинаковые параметры шаблона, соответственно, как P или A.
  • Каждый шаблон функции имеет один параметр функци��, тип которого является специализацией X с аргументами шаблона, соответствующими параметрам шаблона из соответствующего шаблона функции, где для каждого параметра шаблона PP в списке параметров шаблона шаблона функции, формируется соответствующий аргумент шаблона AA. Если PP объявляет пакет параметров, то AA является расширением пакета PP...; иначе, (начиная с C++11) AA является выражением-идентификатором PP.

Если в результате перезаписи получается недопустимый тип, то P не является, по крайней мере, таким специализированным, как A.

template<typename T>
struct eval;                     // основной шаблон
 
template<template<typename, typename...> class TT, typename T1, typename... Rest>
struct eval<TT<T1, Rest...>> {}; // частичная специализация eval
 
template<typename T1> struct A;
template<typename T1, typename T2> struct B;
template<int N> struct C;
template<typename T1, int N> struct D;
template<typename T1, typename T2, int N = 17> struct E;
 
eval<A<int>> eA;        // OK: соответствует частичной специализации eval
eval<B<int, float>> eB; // OK: соответствует частичной специализации eval
eval<C<17>> eC;         // ошибка: C не соответствует TT в частичной специализации
                        // потому что первый параметр TT является
                        // параметром шаблона типом, а 17 не именует тип
eval<D<int, 17>> eD;    // ошибка: D не соответствует TT в частичной специализации
                        // потому что второй параметр TT это пакет
                        // параметров типа, а 17 не именует тип
eval<E<int, float>> eE; // ошибка: E не соответствует TT в частичной специализации,
                        // потому что третий параметр E (по умолчанию) не является типом

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

Хотя на это было указано очень рано (CWG#150), к тому времени, когда оно было решено, изменения были внесены в рабочий документ C++17 и резолюция стала функциональностью де-факто C++17. Многие компиляторы отключают её по умолчанию:

  • GCC по умолчанию отключает её во всех языковых режимах до C++17, её можно включить, только установив флаг компилятора в этих режимах.
  • Clang по умолчанию отключает её во всех языковых режимах, её можно включить, только установив флаг компилятора.
  • Microsoft Visual Studio рассматривает её как обычную функциональность C++17 и включает только в языковых режимах C++17 и более поздних версиях (т.е. не поддерживает языковой режим C++14, который является режимом по умолчанию).
template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template<class... Types> class C { /* ... */ };
 
template<template<class> class P> class X { /* ... */ };
X<A> xa; // OK
X<B> xb; // OK после P0522R0
         // Ошибка ранее: не точное совпадение
X<C> xc; // OK после P0522R0
         // Ошибка ранее: не точное совпадение
 
template<template<class...> class Q> class Y { /* ... */ };
Y<A> ya; // OK
Y<B> yb; // OK
Y<C> yc; // OK
 
template<auto n> class D { /* ... */ };   // примечание: C++17
template<template<int> class R> class Z { /* ... */ };
Z<D> zd; // OK после P0522R0: параметр шаблона является
         // более специализированным, чем аргумент шаблона
 
template<int> struct SI { /* ... */ };
template<template<auto> class> void FA(); // примечание: C++17
FA<SI>(); // Ошибка

[править] Аргументы шаблона по умолчанию

Аргументы шаблона по умолчанию указываются в списках параметров после знака =. Значения по умолчанию можно указать для любого типа параметра шаблона (типа, не типа или шаблона), но не для пакетов параметров (начиная с C++11).

Если значение по умолчанию указано для параметра шаблона шаблона основного класса , шаблона первичной переменной, (начиная с C++14) или псевдонима шаблона, каждый последующий параметр шаблона должен иметь аргумент по умолчанию, за исключением того, что последний может быть пакетом параметров шаблона (начиная с C++11). В шаблоне функции нет ограничений на параметры, которые следуют за значением по умолчанию, а за пакетом параметров могут следовать дополнительные параметры типа, только если они имеют значения по умолчанию или могут быть выведены из аргументов функции (начиная с C++11).

Параметры по умолчанию не разрешены

(до C++11)

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

(начиная с 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; // Аргумент по умолчанию для U создаётся в этот момент
            // тип p есть S<bool, int>*
(начиная с C++14)

[править] Эквивалентность аргументов шаблона

Эквивалентность аргументов шаблона используется для определения того, являются ли два идентификатора шаблона одинаковыми.

Два значения эквивалентные-аргументы-шаблона, если они одного типа и

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

[править] Примечание

Макрос тест функциональности Значение Стандарт Комментарий
__cpp_nontype_template_parameter_auto 201606L (C++17) Объявление параметра шаблона, не являющегося типом с auto
__cpp_template_template_args 201611L (c++17) Соответствие аргументов шаблона шаблона
__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++:

Номер Применён Поведение в стандарте Корректное поведение
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 для переменных шаблонов не было ограничения на параметры
шаблона после параметра шаблона с аргументом по умолчанию
применено то же ограничение, что и для
шаблонов классов и шаблонов псевдонимов