Namespaces
Variants

Class template argument deduction (CTAD) (since C++17)

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

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

  • любое объявление , которое задает инициализацию переменной и variable template, чей объявленный тип является шаблоном класса (возможно cv-qualified ):
std::pair p(2, 4.5);     // выводится как std::pair<int, double> p(2, 4.5);
std::tuple t(4, 3, 2.5); // аналогично auto t = std::make_tuple(4, 3, 2.5);
std::less l;             // аналогично std::less<void> l;
template<class T>
struct A
{
    A(T, T);
};
auto y = new A{1, 2}; // выделенный тип - A<int>
auto lck = std::lock_guard(mtx);     // выводится как std::lock_guard<std::mutex>
std::copy_n(vi1, 3,
    std::back_insert_iterator(vi2)); // выводится как std::back_insert_iterator<T>,
                                     // где T - тип контейнера vi2
std::for_each(vi.begin(), vi.end(),
    Foo([&](int i) {...}));          // выводится как Foo<T>,
                                     // где T - уникальный тип лямбда-выражения
template<class T>
struct X
{
    constexpr X(T) {}
};
template<X x>
struct Y {};
Y<0> y; // OK, Y<X<int>(0)>
(since C++20)

Содержание

Дедукция для шаблонов классов

Неявно генерируемые направляющие вывода

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

  • Если C определен, для каждого конструктора (или шаблона конструктора) C i объявленного в указанном основном шаблоне, создается фиктивная шаблонная функция F i , удовлетворяющая всем следующим условиям:
  • Шаблонные параметры F i — это шаблонные параметры C с последующим (если C i является шаблоном конструктора) добавлением шаблонных параметров C i (аргументы по умолчанию для шаблонов также включаются).
  • Ассоциированные ограничения F i являются конъюнкцией ассоциированных ограничений C и ассоциированных ограничений C i .
(начиная с C++20)
  • Список параметров функции F i является списком параметров класса C i .
  • Возвращаемый тип функции F i представляет собой C с последующими шаблонными параметрами шаблона класса, заключенными в <> .
  • Если C не определен или не объявляет никаких конструкторов, добавляется дополнительная фиктивная шаблонная функция, производная, как указано выше, от гипотетического конструктора C() .
  • В любом случае, добавляется дополнительная фиктивная шаблонная функция, выведенная, как описано выше, из гипотетического конструктора C(C) , называемая кандидатом для вывода копирования.
  • Список параметров F i является списком параметров G i .
  • Возвращаемый тип F i является простым шаблонным идентификатором G i .
  • Если G i имеет параметры шаблона (синтаксис (2) ), F i является шаблоном функции, и его список параметров шаблона является списком параметров шаблона G i . В противном случае, F i является функцией.
  • Кроме того, если
  • C определен и удовлетворяет требованиям агрегатного типа с предположением, что любая зависимая базовый класс не имеет виртуальных функций или виртуальных базовых классов,
  • нет пользовательских deduction guides для C , и
  • переменная инициализируется из непустого списка инициализаторов arg1, arg2, ..., argn (которые могут использовать designated initializer ),
может быть добавлен агрегатный кандидат вывода. Список параметров агрегатного кандидата вывода формируется из типов элементов агрегата следующим образом:
  • brace elision не рассматривается для любого агрегатного элемента, который имеет
  • зависимый не-массивный тип,
  • массивный тип с value-dependent границей, или
  • массивный тип с зависимым типом элемента массива и arg i является string literal
  • если C (или его элемент, который сам является агрегатом) имеет базовый класс, который является pack expansion :
  • если pack expansion является завершающим агрегатным элементом, он считается соответствующим всем оставшимся элементам списка инициализации;
  • иначе, pack считается пустым.
  • Если такого e i не существует, агрегатный кандидат вывода не добавляется.
  • Иначе, определяется список параметров T 1 , T 2 , ..., T n агрегатного кандидата вывода следующим образом:
  • Если e i является массивом и arg i является braced-init-list , T i является rvalue ссылкой на объявленный тип e i .
  • Если e i является массивом и arg i является string literal , T i является lvalue ссылкой на const-квалифицированный объявленный тип e i .
  • Иначе, T i является объявленным типом e i .
  • Если pack был пропущен, потому что он является незавершающим агрегатным элементом, дополнительный parameter pack вида P j ... вставляется в его исходную позицию агрегатного элемента. (Это, как правило, приведет к неудаче вывода.)
  • Если pack является завершающим агрегатным элементом, завершающая последовательность параметров, соответствующая ему, заменяется единственным параметром вида T n ... .
Агрегатный кандидат вывода является вымышленным шаблоном функции, полученным как описано выше из гипотетического конструктора C(T 1 , T 2 , ..., T n ) .
Во время вывода аргументов шаблона для агрегатного кандидата вывода количество элементов в завершающем parameter pack выводится только из количества оставшихся аргументов функции, если оно не выведено иным образом.
template<class T>
struct A
{
    T t;
    struct
    {
        long a, b;
    } u;
};
A a{1, 2, 3};
// aggregate deduction candidate:
//   template<class T>
//   A<T> F(T, long, long);
template<class... Args>
struct B : std::tuple<Args...>, Args... {};
B b{std::tuple<std::any, std::string>{}, std::any{}};
// aggregate deduction candidate:
//   template<class... Args>
//   B<Args...> F(std::tuple<Args...>, Args...);
// type of b is deduced as B<std::any, std::string>
(начиная с C++20)

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

Эти вымышленные конструкторы являются открытыми членами гипотетического типа класса. Они являются explicit, если руководство было сформировано из explicit конструктора. Если разрешение перегрузки завершается неудачей, программа является некорректной. В противном случае возвращаемый тип выбранной специализации шаблона F становится выведенной специализацией шаблона класса.

template<class T>
struct UniquePtr
{
    UniquePtr(T* t);
};
UniquePtr dp{new auto(2.0)};
// Один объявленный конструктор:
// C1: UniquePtr(T*);
// Набор неявно сгенерированных направляющих вывода:
// F1: template<class T>
//     UniquePtr<T> F(T* p);
// F2: template<class T> 
//     UniquePtr<T> F(UniquePtr<T>); // кандидат копирующего вывода
// воображаемый класс для инициализации:
// struct X
// {
//     template<class T>
//     X(T* p);         // из F1
//     
//     template<class T>
//     X(UniquePtr<T>); // из F2
// };
// прямая инициализация объекта X
// с "new double(2.0)" в качестве инициализатора
// выбирает конструктор, соответствующий направляющей F1 с T = double
// Для F1 с T=double, возвращаемый тип - UniquePtr<double>
// результат:
// UniquePtr<double> dp{new auto(2.0)}

Или, для более сложного примера (примечание: " S::N " не скомпилируется: квалификаторы разрешения области видимости не могут быть выведены):

template<class T>
struct S
{
    template<class U>
    struct N
    {
        N(T);
        N(T, U);
        template<class V>
        N(V, U);
    };
};
S<int>::N x{2.0, 1};
// неявно сгенерированные направляющие вывода (заметим, что T уже известно как int)
// F1: template<class U>
//     S<int>::N<U> F(int);
// F2: template<class U>
//     S<int>::N<U> F(int, U);
// F3: template<class U, class V>
//     S<int>::N<U> F(V, U);
// F4: template<class U>
//     S<int>::N<U> F(S<int>::N<U>); (кандидат копирующего вывода)
// Разрешение перегрузки для direct-list-init с "{2.0, 1}" в качестве инициализатора
// выбирает F3 с U=int и V=double.
// Возвращаемый тип - S<int>::N<int>
// результат:
// S<int>::N<int> x{2.0, 1};

Пользовательские правила вывода

Синтаксис пользовательского руководства по выводу представляет собой синтаксис объявления функции (шаблона) с завершающим возвращаемым типом, за исключением того, что в качестве имени функции используется имя шаблона класса:

explicit  (необязательно) template-name ( parameter-list ) -> simple-template-id requires-clause  (необязательно) ; (1)
template < template-parameter-list  > requires-clause  (необязательно)
explicit  (необязательно) template-name ( parameter-list ) -> simple-template-id requires-clause  (необязательно) ;
(2)
template-parameter-list - непустой разделённый запятыми список параметров шаблона
explicit - спецификатор explicit
template-name - имя шаблона класса, аргументы которого должны быть выведены
parameter-list - (возможно, пустой) список параметров
simple-template-id - простой идентификатор шаблона
requires-clause - (начиная с C++20) условие requires


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

(since C++20)

Определяемые пользователем правила вывода должны называть шаблон класса и должны быть введены в той же семантической области видимости шаблона класса (которой может быть пространство имен или объемлющий класс) и, для шаблона класса-члена, должны иметь тот же уровень доступа, но правила вывода не становятся членами этой области видимости.

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

// объявление шаблона
template<class T>
struct container
{
    container(T t) {}
    template<class Iter>
    container(Iter beg, Iter end);
};
// дополнительное руководство по выводу
template<class Iter>
container(Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
// использование
container c(7); // OK: выводит T=int с использованием неявно сгенерированного руководства
std::vector<double> v = {/* ... */};
auto d = container(v.begin(), v.end()); // OK: выводит T=double
container e{5, 6}; // Ошибка: не существует std::iterator_traits<int>::value_type

Фиктивные конструкторы для целей разрешения перегрузки (описанные выше) являются явными, если они соответствуют неявно сгенерированному направляющему выводу, образованному из явного конструктора, или пользовательскому направляющему выводу, который объявлен explicit . Как всегда, такие конструкторы игнорируются в контексте копирующей инициализации:

template<class T>
struct A
{
    explicit A(const T&, ...) noexcept; // #1
    A(T&&, ...);                        // #2
};
int i;
A a1 = {i, i}; // ошибка: невозможно вывести из rvalue-ссылки в #2,
               // и #1 является explicit и не рассматривается при copy-инициализации.
A a2{i, i};    // OK, #1 выводит A<int> и также инициализирует
A a3{0, i};    // OK, #2 выводит A<int> и также инициализирует
A a4 = {0, i}; // OK, #2 выводит A<int> и также инициализирует
template<class T>
A(const T&, const T&) -> A<T&>; // #3
template<class T>
explicit A(T&&, T&&)  -> A<T>;  // #4
A a5 = {0, 1}; // ошибка: #3 выводит A<int&>
               // и #1 & #2 приводят к одинаковым конструкторам параметров.
A a6{0, 1};    // OK, #4 выводит A<int> и #2 инициализирует
A a7 = {0, i}; // ошибка: #3 выводит A<int&>
A a8{0, i};    // ошибка: #3 выводит A<int&>
// Примечание: проверьте https://github.com/cplusplus/CWG/issues/647, утверждающий, что
// примеры a7 и a8 некорректны и возможно должны быть заменены на
//A a7 = {0, i}; // ошибка: #2 и #3 оба подходят, разрешение перегрузки завершается неудачей
//A a8{i,i};     // ошибка: #3 выводит A<int&>,
//               //        #1 и #2 объявляют одинаковый конструктор

Использование члена-typedef или псевдонима шаблона в списке параметров конструктора или шаблона конструктора само по себе не делает соответствующий параметр невыводимым контекстом в неявно сгенерированном руководстве.

template<class T>
struct B
{
    template<class U>
    using TA = T;
    template<class U>
    B(U, TA<U>); // #1
};
// Неявно сгенерированное правило вывода из #1 эквивалентно
//     template<class T, class U>
//     B(U, T) -> B<T>;
// а не
//     template<class T, class U>
//     B(U, typename B<T>::template TA<U>) -> B<T>;
// что не поддавалось бы выводу
B b{(int*)0, (char*)0}; // OK, выводит B<char*>

Вывод для псевдонимов шаблонов

Когда приведение в стиле функции или объявление переменной использует имя псевдонима шаблона A без списка аргументов в качестве спецификатора типа, где A определен как псевдоним для B<ArgList> , область видимости B является независимой, и B является либо шаблоном класса, либо аналогично определенным псевдонимом шаблона, вывод будет выполняться так же, как и для шаблонов классов, за исключением того, что направляющие вместо этого генерируются из направляющих B , следующим образом:

  • Для каждой направляющей f из B выведите аргументы шаблона возвращаемого типа f из B<ArgList> с использованием вывода аргументов шаблона , за исключением того, что вывод не завершается неудачей, если некоторые аргументы не выведены. Если вывод завершается неудачей по другой причине, продолжите с пустым набором выведенных аргументов шаблона.
  • Подставьте результат вышеуказанного вывода в f , если подстановка завершается неудачей, направляющая не создается; в противном случае, пусть g обозначает результат подстановки, формируется направляющая f' , такая что
  • Типы параметров и возвращаемый тип f' совпадают с g
  • Если f является шаблоном, f' является шаблоном функции, чей список параметров шаблона состоит из всех параметров шаблона A (включая их аргументы шаблона по умолчанию), которые появляются в вышеуказанных выводах или (рекурсивно) в их аргументах шаблона по умолчанию, за которыми следуют параметры шаблона f , которые не были выведены (включая их аргументы шаблона по умолчанию); в противном случае ( f не является шаблоном), f' является функцией
  • Связанные ограничения f' являются конъюнкцией связанных ограничений g и ограничения, которое выполняется тогда и только тогда, когда аргументы A выводимы из результирующего типа
template<class T>
class unique_ptr
{
    /* ... */
};
template<class T>
class unique_ptr<T[]>
{
    /* ... */
};
template<class T>
unique_ptr(T*) -> unique_ptr<T>;   // #1
template<class T>
unique_ptr(T*) -> unique_ptr<T[]>; // #2
template<class T>
concept NonArray = !std::is_array_v<T>;
template<NonArray A>
using unique_ptr_nonarray = unique_ptr<A>;
template<class A>
using unique_ptr_array = unique_ptr<A[]>;
// сгенерированная направляющая для unique_ptr_nonarray:
// из #1 (вывод unique_ptr<T> из unique_ptr<A> дает T = A):
// template<class A>
//     requires(argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<A>>)
// auto F(A*) -> unique_ptr<A>;
// из #2 (вывод unique_ptr<T[]> из unique_ptr<A> ничего не дает):
// template<class T>
//     requires(argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<T[]>>)
// auto F(T*) -> unique_ptr<T[]>;
// где argument_of_unique_ptr_nonarray_is_deducible_from может быть определен как
// template<class>
// class AA;
// template<NonArray A>
// class AA<unique_ptr_nonarray<A>> {};
// template<class T>
// concept argument_of_unique_ptr_nonarray_is_deducible_from =
//     requires { sizeof(AA<T>); };
// сгенерированная направляющая для unique_ptr_array:
// из #1 (вывод unique_ptr<T> из unique_ptr<A[]> дает T = A[]):
// template<class A>
//     requires(argument_of_unique_ptr_array_is_deducible_from<unique_ptr<A[]>>)
// auto F(A(*)[]) -> unique_ptr<A[]>;
// из #2 (вывод unique_ptr<T[]> из unique_ptr<A[]> дает T = A):
// template<class A>
//     requires(argument_of_unique_ptr_array_is_deducible_from<unique_ptr<A[]>>)
// auto F(A*) -> unique_ptr<A[]>;
// где argument_of_unique_ptr_array_is_deducible_from может быть определен как
// template<class>
// class BB;
// template<class A>
// class BB<unique_ptr_array<A>> {};
// template<class T>
// concept argument_of_unique_ptr_array_is_deducible_from =
//     requires { sizeof(BB<T>); };
// Использование:
unique_ptr_nonarray p(new int); // выведено в unique_ptr<int>
// направляющая вывода, сгенерированная из #1, возвращает unique_ptr<int>
// направляющая вывода, сгенерированная из #2, возвращает unique_ptr<int[]>, которая игнорируется, потому что
//   argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<int[]>> не выполняется
unique_ptr_array q(new int[42]); // выведено в unique_ptr<int[]>
// направляющая вывода, сгенерированная из #1, завершается неудачей (не может вывести A в A(*)[] из new int[42])
// направляющая вывода, сгенерированная из #2, возвращает unique_ptr<int[]>
(начиная с C++20)

Примечания

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

std::tuple t1(1, 2, 3);                // OK: выведение типов
std::tuple<int, int, int> t2(1, 2, 3); // OK: все аргументы предоставлены
std::tuple<> t3(1, 2, 3);    // Ошибка: нет подходящего конструктора в tuple<>.
                             //        Выведение типов не выполняется.
std::tuple<int> t4(1, 2, 3); // Ошибка

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

template<class A, class B>
struct Agg
{
    A a;
    B b;
};
// implicitly-generated guides are formed from default, copy, and move constructors
template<class A, class B>
Agg(A a, B b) -> Agg<A, B>;
// ^ This deduction guide can be implicitly generated in C++20
Agg agg{1, 2.0}; // deduced to Agg<int, double> from the user-defined guide
template<class... T>
array(T&&... t) -> array<std::common_type_t<T...>, sizeof...(T)>;
auto a = array{1, 2, 5u}; // deduced to array<unsigned, 3> from the user-defined guide
(до C++20)

Пользовательские направляющие вывода не обязаны быть шаблонами:

template<class T>
struct S
{
    S(T);
};
S(char const*) -> S<std::string>;
S s{"hello"}; // выводится как S<std::string>

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

template<class T>
struct X
{
    X(T) {}
    template<class Iter>
    X(Iter b, Iter e) {}
    template<class Iter>
    auto foo(Iter b, Iter e)
    {
        return X(b, e); // нет вывода: X - это текущий X<T>
    }
    template<class Iter>
    auto bar(Iter b, Iter e)
    {
        return X<typename Iter::value_type>(b, e); // необходимо указать, что мы хотим
    }
    auto baz()
    {
        return ::X(0); // не введенное имя класса; выводится как X<int>
    }
};

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

template<class T>
struct A
{
    A(T, int*);     // #1
    A(A<T>&, int*); // #2
    enum { value };
};
template<class T, int N = T::value>
A(T&&, int*) -> A<T>; //#3
A a{1, 0}; // использует #1 для вывода A<int> и инициализирует с помощью #1
A b{a, 0}; // использует #2 (более специализирован, чем #3) для вывода A<int> и инициализирует с помощью #2

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

  • Шаблон функции, созданный из пользовательского руководства по выводу, предпочтительнее шаблона, неявно сгенерированного из конструктора или шаблона конструктора.
  • Кандидат копирующего вывода предпочтительнее всех других шаблонов функций, неявно сгенерированных из конструктора или шаблона конструктора.
  • Шаблон функции, неявно сгенерированный из нешаблонного конструктора, предпочтительнее шаблона функции, неявно сгенерированного из шаблона конструктора.
template<class T>
struct A
{
    using value_type = T;
    A(value_type); // #1
    A(const A&);   // #2
    A(T, T, int);  // #3
    template<class U>
    A(int, T, U);  // #4
};                 // #5, кандидат копирующего вывода A(A);
A x(1, 2, 3); // использует #3, сгенерированный из нешаблонного конструктора
template<class T>
A(T) -> A<T>; // #6, менее специализирован чем #5
A a(42); // использует #6 для вывода A<int> и #1 для инициализации
A b = a; // использует #5 для вывода A<int> и #2 для инициализации
template<class T>
A(A<T>) -> A<A<T>>; // #7, такой же специализированный как #5
A b2 = a; // использует #7 для вывода A<A<int>> и #1 для инициализации

Ссылка на rvalue на неквалифицированный cv шаблонный параметр не является пересылающей ссылкой если этот параметр является параметром шаблона класса:

template<class T>
struct A
{
    template<class U>
    A(T&&, U&&, int*); // #1: T&& не является forwarding reference
                       //     U&& является forwarding reference
    A(T&&, int*);      // #2: T&& не является forwarding reference
};
template<class T>
A(T&&, int*) -> A<T>; // #3: T&& является forwarding reference
int i, *ip;
A a{i, 0, ip};  // ошибка, невозможно вывести из #1
A a0{0, 0, ip}; // использует #1 для вывода A<int> и #1 для инициализации
A a2{i, ip};    // использует #3 для вывода A<int&> и #2 для инициализации

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

std::tuple t1{1};  //std::tuple<int>
std::tuple t2{t1}; //std::tuple<int>, а не std::tuple<std::tuple<int>>
std::vector v1{1, 2};   // std::vector<int>
std::vector v2{v1};     // std::vector<int>, а не std::vector<std::vector<int>> (P0702R1)
std::vector v3{v1, v2}; // std::vector<std::vector<int>>

За исключением особого случая с копированием и обёртыванием, строгое предпочтение конструкторов со списками инициализации в list-initialization остаётся неизменным.

std::vector v1{1, 2}; // std::vector<int>
std::vector v2(v1.begin(), v1.end()); // std::vector<int>
std::vector v3{v1.begin(), v1.end()}; // std::vector<std::vector<int>::iterator>

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

std::tuple p1{1, 1.0};             //std::tuple<int, double>, с использованием выведения типа
auto p2 = std::make_tuple(1, 1.0); //std::tuple<int, double>, до C++17
Макрос тестирования возможностей Значение Стандарт Возможность
__cpp_deduction_guides 201703L (C++17) Вывод аргументов шаблона для шаблонов классов
201907L (C++20) CTAD для агрегатов и псевдонимов

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.

DR Applied to Behavior as published Correct behavior
CWG 2376 C++17 CTAD выполнялся даже если тип объявляемой переменной
отличается от шаблона класса, аргументы которого выводятся
не выполнять
CTAD в этом случае
CWG 2628 C++20 неявные направляющие вывода не распространяли ограничения распространять ограничения
CWG 2697 C++20 было неясно, разрешен ли сокращенный синтаксис шаблона функции
в пользовательских направляющих вывода
запрещено
CWG 2707 C++20 направляющие вывода не могли иметь завершающую requires клаузу могут
CWG 2714 C++17 неявные направляющие вывода не учитывали
аргументы по умолчанию конструкторов
учитывать их
CWG 2913 C++20 решение CWG issue 2707 сделало синтаксис направляющих вывода
несогласованным с синтаксисом объявления функции
скорректирован синтаксис
P0702R1 C++17 конструктор initializer-list может опережать
кандидата вывода копирования, приводя к обертыванию
фаза initializer-list
пропускается при копировании