C++17 클래스 템플릿 인수 추론(CTAD)과 Deduction Guides
C++17 이전에는 클래스 템플릿을 사용할 때 항상 타입 인수를 명시해야 했습니다. std::pair<int, double>처럼 길고 중복적인 코드가 필요했습니다. C++17의 **CTAD(Class Template Argument Deduction)**는 생성자 인수에서 템플릿 인수를 자동으로 추론하여 이 문제를 해결합니다.
1. 기본 동작
섹션 제목: “1. 기본 동작”// C++14 이전: 명시적 타입 지정 필요std::pair<int, double> p1(42, 3.14);auto p2 = std::make_pair(42, 3.14); // 헬퍼 함수 필요
// C++17: 생성자에서 자동 추론std::pair p3(42, 3.14); // pair<int, double>로 추론std::vector v{1, 2, 3, 4}; // vector<int>로 추론std::array arr{1, 2, 3}; // array<int, 3>으로 추론2. 추론 규칙
섹션 제목: “2. 추론 규칙”컴파일러는 생성자를 분석하여 추론 가이드를 자동으로 생성합니다. 가장 적합한 오버로드를 선택하는 방식입니다.
template<typename T>struct Box { T value; Box(T v) : value(v) {}};
Box b1(42); // Box<int>Box b2(3.14); // Box<double>Box b3("hello"); // Box<const char*>3. 명시적 Deduction Guide
섹션 제목: “3. 명시적 Deduction Guide”기본 추론이 원하는 타입을 유추하지 못할 때 명시적 deduction guide를 작성합니다.
template<typename T>struct Stack { std::vector<T> data;};
// 초기화 리스트로부터 추론하는 가이드template<typename T>Stack(std::initializer_list<T>) -> Stack<T>;
Stack s{1, 2, 3}; // Stack<int>3.1 문자열 리터럴 처리
섹션 제목: “3.1 문자열 리터럴 처리”template<typename T>struct Wrapper { T value; Wrapper(T v) : value(v) {}};
// const char* 대신 std::string으로 추론하도록 가이드Wrapper(const char*) -> Wrapper<std::string>;
Wrapper w("hello"); // Wrapper<std::string> (const char* 아님)3.2 이터레이터 범위 추론
섹션 제목: “3.2 이터레이터 범위 추론”template<typename Iter>struct Range { Iter begin_, end_; Range(Iter b, Iter e) : begin_(b), end_(e) {}};
// 이터레이터 쌍에서 원소 타입 추론template<typename Iter>Range(Iter, Iter) -> Range<Iter>;
std::vector<int> v{1, 2, 3};Range r(v.begin(), v.end()); // Range<std::vector<int>::iterator>4. 표준 라이브러리 적용 예
섹션 제목: “4. 표준 라이브러리 적용 예”4.1 std::pair
섹션 제목: “4.1 std::pair”// 이전 방식auto p1 = std::make_pair(1, std::string("hello"));std::pair<int, std::string> p2{1, "hello"};
// C++17std::pair p3{1, std::string("hello")}; // pair<int, string>std::pair p4{1, "hello"}; // pair<int, const char*>4.2 std::tuple
섹션 제목: “4.2 std::tuple”// 이전auto t1 = std::make_tuple(1, 2.0, "three");
// C++17std::tuple t2{1, 2.0, "three"}; // tuple<int, double, const char*>4.3 std::optional
섹션 제목: “4.3 std::optional”std::optional opt1{42}; // optional<int>std::optional opt2{"hello"}; // optional<const char*>4.4 std::function
섹션 제목: “4.4 std::function”// function은 CTAD 지원이 제한적 — 람다로 직접 초기화 불가// std::function f = [](int x) { return x; }; // C++17에서 여전히 불가// 명시적 타입 필요std::function<int(int)> f = [](int x) { return x; };5. 집합체 초기화와 CTAD (C++20)
섹션 제목: “5. 집합체 초기화와 CTAD (C++20)”C++20에서는 집합체(aggregate) 타입도 CTAD를 지원합니다.
template<typename T, typename U>struct Pair { T first; U second;};
// C++20: aggregate deduction guide 자동 생성Pair p{1, 2.0}; // Pair<int, double>6. 실전 활용 — 타입 안전 빌더 패턴
섹션 제목: “6. 실전 활용 — 타입 안전 빌더 패턴”template<typename... Ts>struct TypeList { using types = std::tuple<Ts...>;};
template<typename T>struct Builder { std::vector<T> items;
Builder& add(T item) { items.push_back(std::move(item)); return *this; }};
// 첫 인수에서 T를 추론template<typename T>Builder(T) -> Builder<T>;
auto b = Builder(42).add(10).add(20); // Builder<int>7. CTAD가 동작하지 않는 경우
섹션 제목: “7. CTAD가 동작하지 않는 경우”// 1. 부분 특수화: 전체 인수 추론만 가능template<typename T, typename U>struct Pair {};
// 2. 별칭 템플릿: C++20 이전에는 불가template<typename T>using Vec = std::vector<T>;// Vec v{1,2,3}; // C++20 이전 오류
// 3. 상속: 기본 클래스 생성자로만 추론template<typename T>struct MyVector : std::vector<T> { using std::vector<T>::vector;};// MyVector v{1,2,3}; // 추론 실패 (C++20 이전)8. 주의사항
섹션 제목: “8. 주의사항”// 주의1: 이니셜라이저 리스트 vs 생성자 인수std::vector v1(5, 0); // vector<int>: 5개의 0std::vector v2{5, 0}; // vector<int>: {5, 0}
// 주의2: 예상과 다른 추론std::pair p{"hello", "world"}; // pair<const char*, const char*>// std::string으로 추론되지 않음 — 명시적 가이드 필요
// 주의3: 중괄호 초기화와 initializer_list 우선순위std::vector v3{1, 2, 3}; // initializer_list<int> — vector<int>std::vector v4(3, 1); // 생성자(count, value) — vector<int> 3개의 1| 기능 | C++14 | C++17 | C++20 |
|---|---|---|---|
| 함수 템플릿 추론 | O | O | O |
| 클래스 템플릿 추론 | X | O | O (aggregate 포함) |
| 명시적 deduction guide | X | O | O |
| 별칭 템플릿 CTAD | X | X | O |
CTAD는 코드의 중복을 줄이고 make_* 헬퍼 함수의 필요성을 낮춥니다. 다만, 예상치 못한 타입으로 추론될 수 있으므로 명시적 deduction guide와 함께 활용하는 것이 안전합니다.