Spec-Zone.ru › C++

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

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

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

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

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

  • параметром шаблона нетипового типа;
  • параметром шаблона типового типа;
  • параметром шаблона шаблона.

Параметр шаблона нетипового типа

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

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

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

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

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

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

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

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)

Идентификатор, который называет параметр шаблона нетипового типа типа класса 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)

Параметр шаблона типового типа

ключ параметра типа имя (опционально) (1)
ключ параметра типа имя (опционально) = значение по умолчанию (2)
ключ параметра типа ... имя (опционально) (3) (с C++11)
ограничение типа имя (опционально) (4) (с C++20)
ограничение типа имя (опционально) = значение по умолчанию (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 { /* ... */ };

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

// Declarations of the templates shown above:
template<class>
class My_vector;
template<class = void>
struct My_op_functor;
template<typename...>
class My_tuple;

В теле объявления шаблона имя параметра типа — это псевдоним типа, который соответствует типу, предоставленному при инстанцировании шаблона.

Каждый ограниченный параметр 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 < список параметров > ключ параметра типа имя (необязательно) (1)
template < список параметров > ключ параметра типа имя (необязательно) = значение по умолчанию (2)
template < список параметров > ключ параметра типа ... имя (необязательно) (3) (с C++11)
ключ параметра типа - 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), либо заданы по умолчанию. Для шаблонов функций аргументы явно указаны, выведены из контекста или заданы по умолчанию.

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

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
}

Нетиповые аргументы шаблонов

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

  • Для целочисленных и арифметических типов аргумент шаблона, используемый при инстанцировании, должен быть преобразованным константным выражением типа параметра шаблона (применяется определённое неявное преобразование).
  • Для указателей на объекты аргументы шаблонов должны указывать на адрес целого объекта со статической длиной хранения и связью (внутренней или внешней), или константное выражение, которое вычисляется до соответствующего нулевого указателя или 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: 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), которые не могут ссылаться на/иметь адрес

  • временного объекта (включая созданный во время инициализации ссылки);
  • строкового литерала;
  • результата typeid;
  • предопределённой переменной __func__;
  • или подобъекта (включая нестатический член класса, базовый подобъект или элемент массива) одного из вышеперечисленных(с 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)

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

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

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

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

Примечания

В параметрах шаблона ограничения типов могут использоваться как для параметров типов, так и для параметров нетипа, в зависимости от наличия 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) Типы классов и типы с плавающей запятой в параметрах шаблона нетипа

Примеры

#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

Spec-Zone.ru

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