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

class 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)>
(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) 로부터 위와 같이 파생된 추가적인 가상 함수 템플릿이 복사 연역 후보(copy deduction candidate)라고 불리며 추가됩니다.
  • F i 의 매개변수 목록은 G i 의 매개변수 목록입니다.
  • F i 의 반환 타입은 G i 의 단순 템플릿 식별자입니다.
  • 만약 G i 가 템플릿 매개변수를 가지는 경우 (문법 (2) ), F i 는 함수 템플릿이며, 그 템플릿 매개변수 목록은 G i 의 템플릿 매개변수 목록입니다. 그렇지 않으면, F i 는 함수입니다.
  • 추가적으로, 다음 조건을 모두 만족하는 경우
  • C 가 정의되어 있고 모든 종속 기저 클래스에 가상 함수나 가상 기저 클래스가 없다고 가정할 때 집합체 타입 의 요구사항을 충족하며,
  • C 에 대한 사용자 정의 deduction guide가 존재하지 않고,
  • 변수가 비어 있지 않은 초기화자 목록 arg1, arg2, ..., argn ( 지정 초기화자 사용 가능)으로 초기화되는 경우,
집합체 deduction 후보가 추가될 수 있습니다. 집합체 deduction 후보의 매개변수 목록은 집합체 요소 타입들로부터 다음과 같이 생성됩니다:
  • e i arg i 로 초기화되는 (재귀적일 수 있는) 집합체 요소 라고 정의합니다. 단,
  • 다음 중 하나에 해당하는 집합체 요소에 대해서는 brace elision 을 고려하지 않습니다:
  • C (또는 그 자체가 집합체인 요소)가 pack expansion 인 기저 클래스를 가지는 경우:
  • pack expansion이 후행 집합체 요소인 경우, 초기화자 목록의 모든 나머지 요소와 일치하는 것으로 간주합니다;
  • 그렇지 않으면 pack은 비어 있는 것으로 간주합니다.
  • 이러한 e i 가 존재하지 않으면 집합체 deduction 후보를 추가하지 않습니다.
  • 그렇지 않으면, 집합체 deduction 후보의 매개변수 목록 T 1 , T 2 , ..., T n 을 다음과 같이 결정합니다:
  • e i 가 배열이고 arg i braced-init-list 인 경우, T i e i 의 선언된 타입에 대한 rvalue 참조입니다.
  • e i 가 배열이고 arg i 문자열 리터럴 인 경우, T i e i 의 const 한정 선언된 타입에 대한 lvalue 참조입니다.
  • 그렇지 않으면, T i e i 의 선언된 타입입니다.
  • pack이 비후행 집합체 요소여서 건너뛴 경우, 원래 집합체 요소 위치에 P j ... 형태의 추가 매개변수 pack을 삽입합니다. (일반적으로 이는 deduction 실패를 유발합니다.)
  • pack이 후행 집합체 요소인 경우, 이에 해당하는 후행 매개변수 시퀀스는 T n ... 형태의 단일 매개변수로 대체됩니다.
집합체 deduction 후보는 위에서 유도된 가상 생성자 C(T 1 , T 2 , ..., T n ) 로부터 파생된 가상 함수 템플릿입니다.
집합체 deduction 후보에 대한 템플릿 인자 deduction 중에는, 후행 매개변수 pack의 요소 수는 다른 방식으로 deduction되지 않는 경우에만 남은 함수 인자의 수로부터 deduction됩니다.
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 의 특수화에서 파생된 클래스입니다.

이러한 가상 생성자들은 가상의 클래스 타입의 public 멤버입니다. 가이드가 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)"를 초기화자로 사용
// T = double인 F1 가이드에 해당하는 생성자를 선택
// T=double인 F1의 반환 타입은 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>); (복사 추론 후보)
// 직접 목록 초기화에서 "{2.0, 1}"을 초기화자로 사용한 오버로드 해결은
// U=int 및 V=double인 F3을 선택합니다.
// 반환 타입은 S<int>::N<int>입니다
// 결과:
// S<int>::N<int> x{2.0, 1};

사용자 정의 추론 가이드

사용자 정의 추론 가이드의 구문은 후행 반환 타입을 가진 함수 (템플릿) 선언의 구문과 동일하지만, 함수 이름으로 클래스 템플릿의 이름을 사용한다는 점이 다릅니다:

explicit  (optional) template-name ( parameter-list ) -> simple-template-id requires-clause  (optional) ; (1)
template < template-parameter-list  > requires-clause  (optional)
explicit  (optional) template-name ( parameter-list ) -> simple-template-id requires-clause  (optional) ;
(2)
template-parameter-list - 비어 있지 않은 쉼표로 구분된 템플릿 매개변수 목록
explicit - explicit 지정자
template-name - 인자가 추론될 클래스 템플릿의 이름
parameter-list - (비어 있을 수도 있는) 매개변수 목록
simple-template-id - 단순 템플릿 식별자
requires-clause - (C++20부터) requires


사용자 정의 추론 가이드의 매개변수는 플레이스홀더 타입을 가질 수 없습니다: 축약 함수 템플릿 구문은 허용되지 않습니다.

(since C++20)

사용자 정의 추론 가이드는 반드시 클래스 템플릿의 이름을 지정해야 하며, 클래스 템플릿과 동일한 의미론적 범위(네임스페이스나 외부 클래스) 내에서 도입되어야 합니다. 또한 멤버 클래스 템플릿의 경우 동일한 접근 권한을 가져야 하지만, 추론 가이드는 해당 범위의 멤버가 되지 않습니다.

deduction guide는 함수가 아니며 본문을 가지지 않습니다. deduction guide는 이름 검색으로 발견되지 않으며, 클래스 템플릿 인자 추론 시 다른 deduction guide와의 오버로드 해결 을 제외하고는 오버로드 해결에 참여하지 않습니다. deduction guide는 동일한 번역 단위에서 동일한 클래스 템플릿에 대해 재선언될 수 없습니다.

// 템플릿 선언
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}; // 오류: #2에서 rvalue 참조로부터 추론할 수 없으며,
               // #1은 explicit이므로 복사 초기화에서 고려되지 않음
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나 별칭 템플릿을 생성자 또는 생성자 템플릿의 매개변수 목록에서 사용하는 것만으로는, 해당하는 매개변수가 암시적으로 생성되는 가이드에서 비추론 영역(non-deduced context)이 되도록 만들지 않습니다.

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 의 가이드에서 생성됩니다:

  • B 의 각 가이드 f 에 대해, 템플릿 인수 추론 을 사용하여 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[]>;
// generated guide for unique_ptr_nonarray:
// from #1 (deduction of unique_ptr<T> from unique_ptr<A> yields T = A):
// template<class A>
//     requires(argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<A>>)
// auto F(A*) -> unique_ptr<A>;
// from #2 (deduction of unique_ptr<T[]> from unique_ptr<A> yields nothing):
// template<class T>
//     requires(argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<T[]>>)
// auto F(T*) -> unique_ptr<T[]>;
// where argument_of_unique_ptr_nonarray_is_deducible_from can be defined as
// 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>); };
// generated guide for unique_ptr_array:
// from #1 (deduction of unique_ptr<T> from unique_ptr<A[]> yields T = A[]):
// template<class A>
//     requires(argument_of_unique_ptr_array_is_deducible_from<unique_ptr<A[]>>)
// auto F(A(*)[]) -> unique_ptr<A[]>;
// from #2 (deduction of unique_ptr<T[]> from unique_ptr<A[]> yields T = A):
// template<class A>
//     requires(argument_of_unique_ptr_array_is_deducible_from<unique_ptr<A[]>>)
// auto F(A*) -> unique_ptr<A[]>;
// where argument_of_unique_ptr_array_is_deducible_from can be defined as
// 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>); };
// Use:
unique_ptr_nonarray p(new int); // deduced to unique_ptr<int>
// deduction guide generated from #1 returns unique_ptr<int>
// deduction guide generated from #2 returns unique_ptr<int[]>, which is ignored because
//   argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<int[]>> is unsatisfied
unique_ptr_array q(new int[42]); // deduced to unique_ptr<int[]>
// deduction guide generated from #1 fails (cannot deduce A in A(*)[] from new int[42])
// deduction guide generated from #2 returns 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); // 오류

집합체(aggregate)의 클래스 템플릿 인자 추론은 일반적으로 사용자 정의 추론 가이드가 필요합니다:

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>로 추론됨

클래스 템플릿 범위 내에서, 매개변수 목록 없이 템플릿 이름은 삽입된 클래스 이름(injected class name)이며 타입으로 사용될 수 있습니다. 이 경우 클래스 인수 추론(argument deduction)은 발생하지 않으며 템플릿 매개변수는 명시적으로 제공되어야 합니다:

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로 초기화

이전 순위 결정 방식들(부분 순서 지정 포함)이 두 후보 함수 템플릿을 구분하는 데 실패했을 때, 다음 규칙들이 적용됩니다:

  • 사용자 정의 추론 가이드에서 생성된 함수 템플릿은 생성자나 생성자 템플릿에서 암시적으로 생성된 함수 템플릿보다 우선합니다.
  • 복사 추론 후보(copy deduction candidate)는 생성자나 생성자 템플릿에서 암시적으로 생성된 다른 모든 함수 템플릿보다 우선합니다.
  • 비템플릿 생성자에서 암시적으로 생성된 함수 템플릿은 생성자 템플릿에서 암시적으로 생성된 함수 템플릿보다 우선합니다.
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); // A<int> 연역에 #6 사용, 초기화에 #1 사용
A b = a; // A<int> 연역에 #5 사용, 초기화에 #2 사용
template<class T>
A(A<T>) -> A<A<T>>; // #7, #5만큼 특수화됨
A b2 = a; // A<A<int>> 연역에 #7 사용, 초기화에 #1 사용

cv 한정자가 없는 템플릿 매개변수에 대한 rvalue 참조는 해당 매개변수가 클래스 템플릿 매개변수인 경우 forwarding reference 가 아닙니다:

template<class T>
struct A
{
    template<class U>
    A(T&&, U&&, int*); // #1: T&&는 전달 참조가 아님
                       //     U&&는 전달 참조임
    A(T&&, int*);      // #2: T&&는 전달 참조가 아님
};
template<class T>
A(T&&, int*) -> A<T>; // #3: T&&는 전달 참조임
int i, *ip;
A a{i, 0, ip};  // 오류, #1에서 추론 불가
A a0{0, 0, ip}; // A<int> 추론에 #1 사용, 초기화에 #1 사용
A a2{i, ip};    // A<int&> 추론에 #3 사용, 초기화에 #2 사용

단일 인수로부터 초기화할 때 해당 인수가 문제의 클래스 템플릿 특수화인 경우, 기본적으로 래핑보다 복사 추론이 일반적으로 선호됩니다:

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

복사와 래핑의 특별한 경우를 제외하고, 목록 초기화에서 이니셜라이저 리스트 생성자에 대한 강력한 선호도는 그대로 유지됩니다.

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 적용 대상 게시된 동작 올바른 동작
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 이슈 2707 의 해결책이 추론 가이드 구문을
함수 선언 구문과 불일치하게 만듦
구문을 조정함
P0702R1 C++17 초기화자 목록 생성자가 복사 추론 후보를 선점하여
래핑을 초래할 수 있음
복사 시 초기화자 목록 단계
생략됨