C++ CTAD | "클래스 템플릿 인자 추론" 가이드

C++ CTAD | "클래스 템플릿 인자 추론" 가이드

이 글의 핵심

C++ CTAD에 대한 실전 가이드입니다.

들어가며

C++17에서 도입된 CTAD(Class Template Argument Deduction)는 클래스 템플릿 인자를 자동으로 추론하여 코드를 간결하게 만들어줍니다.


1. CTAD 기본

C++14 vs C++17

// C++14: 타입 명시
std::pair<int, double> p(1, 3.14);
std::vector<int> vec = {1, 2, 3};

// C++17: 타입 추론
std::pair p(1, 3.14);
std::vector vec = {1, 2, 3};

표준 라이브러리

#include <vector>
#include <map>
#include <tuple>
#include <array>

int main() {
    std::pair p(1, "Hello");
    std::tuple t(1, 2.0, "Hi");
    std::vector vec = {1, 2, 3};
    std::map m = {{"a", 1}, {"b", 2}};
}

2. 커스텀 클래스 CTAD

기본 예제

#include <iostream>

template<typename T>
class Container {
    T value;
    
public:
    Container(T v) : value(v) {}
    
    T get() const { return value; }
};

int main() {
    Container c(42);
    Container c2(3.14);
    Container c3("Hello");
    
    std::cout << c.get() << std::endl;   // 42
    std::cout << c2.get() << std::endl;  // 3.14
    std::cout << c3.get() << std::endl;  // Hello
}

복사 생성자

#include <iostream>

template<typename T>
class Wrapper {
    T value;
    
public:
    Wrapper(T v) : value(v) {}
    
    Wrapper(const Wrapper& other) : value(other.value) {}
    
    T get() const { return value; }
};

int main() {
    Wrapper w1(42);
    Wrapper w2 = w1;
    
    std::cout << w2.get() << std::endl;  // 42
}

3. 추론 가이드 (Deduction Guides)

기본 추론 가이드

#include <iostream>

template<typename T>
class MyClass {
    T value;
    
public:
    MyClass(T v) : value(v) {}
};

template<typename T>
MyClass(T) -> MyClass<T>;

int main() {
    MyClass obj(42);
    std::cout << "OK" << std::endl;
}

커스텀 추론 가이드

#include <iostream>
#include <string>

template<typename T>
class Container {
    T value;
    
public:
    Container(T v) : value(v) {}
    
    T get() const { return value; }
};

Container(const char*) -> Container<std::string>;

int main() {
    Container c("Hello");
    
    std::cout << c.get() << std::endl;  // Hello
}

복잡한 추론 가이드

#include <iostream>
#include <vector>

template<typename T>
class Matrix {
    std::vector<std::vector<T>> data;
    
public:
    Matrix(std::vector<std::vector<T>> d) : data(d) {}
};

template<typename T>
Matrix(std::vector<std::vector<T>>) -> Matrix<T>;

int main() {
    Matrix m({{1, 2}, {3, 4}});
    std::cout << "OK" << std::endl;
}

4. 자주 발생하는 문제

문제 1: 모호한 추론

#include <iostream>

template<typename T>
class Container {
public:
    Container(T value) {
        std::cout << "T value" << std::endl;
    }
    Container(T* ptr, size_t size) {
        std::cout << "T* ptr" << std::endl;
    }
};

int main() {
    int arr[5];
    
    Container<int> c(arr, 5);
}

문제 2: 초기화 리스트

#include <vector>
#include <iostream>

int main() {
    std::vector vec{1, 2, 3};
    std::vector vec2{vec};
    
    std::cout << vec2.size() << std::endl;  // 1
    
    std::vector vec3(vec);
    
    std::cout << vec3.size() << std::endl;  // 3
}

문제 3: const char*

#include <utility>
#include <string>
#include <iostream>

int main() {
    std::pair p("Hello", "World");
    
    std::pair<std::string, std::string> p2("Hello", "World");
    
    std::cout << p2.first << std::endl;  // Hello
}

5. 추론 비활성화

explicit 생성자

#include <iostream>

template<typename T>
class NoDeduction {
public:
    explicit NoDeduction(T value) {}
};

int main() {
    NoDeduction<int> obj(42);
}

6. 실전 예제

예제 1: 스마트 포인터

#include <memory>
#include <iostream>

template<typename T>
class SmartPtr {
    T* ptr;
    
public:
    SmartPtr(T* p) : ptr(p) {}
    ~SmartPtr() { delete ptr; }
    
    T& operator*() { return *ptr; }
};

template<typename T>
SmartPtr(T*) -> SmartPtr<T>;

int main() {
    SmartPtr p(new int(42));
    std::cout << *p << std::endl;  // 42
}

예제 2: 타입 안전 ID

#include <iostream>
#include <string>

template<typename T>
class TypedId {
    std::string id;
    
public:
    TypedId(std::string i) : id(i) {}
    
    std::string get() const { return id; }
};

template<typename T>
TypedId(std::string) -> TypedId<T>;

struct User {};
struct Post {};

int main() {
    TypedId<User> userId("U001");
    TypedId<Post> postId("P001");
    
    std::cout << userId.get() << std::endl;  // U001
}

정리

핵심 요약

  1. CTAD: C++17 클래스 템플릿 인자 추론
  2. 추론 가이드: 커스텀 추론 규칙
  3. 표준 라이브러리: pair, vector, map 등
  4. 성능: 컴파일 타임, 런타임 오버헤드 없음

CTAD 장단점

장점단점
간결한 코드타입 명확성 감소
타입 안전모호한 추론 가능
컴파일 타임C++17 이상 필요

실전 팁

  • 타입이 명확할 때 CTAD 사용
  • 모호한 경우 명시적 타입 지정
  • 추론 가이드로 커스터마이징
  • const char*std::string으로 변환 고려

다음 단계

  • C++ Deduction Guides
  • C++ if constexpr
  • C++ Template

관련 글

  • C++ 템플릿 인자 추론 | template argument deduction 가이드