본문으로 건너뛰기 C++ CTAD | '클래스 템플릿 인자 추론' 가이드

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

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

이 글의 핵심

C++17 CTAD로 pair·vector 등 클래스 템플릿 인자를 생략하는 방법. 사용자 정의 클래스와 추론 가이드(deduction guide) 실무 팁을 정리합니다.

들어가며

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


1. CTAD 기본

C++14 vs C++17

C/C++ 예제 코드입니다.

// 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으로 변환 고려

다음 단계


관련 글


자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. C++17 CTAD로 pair·vector 등 클래스 템플릿 인자를 생략하는 방법. 사용자 정의 클래스와 추론 가이드(deduction guide) 실무 팁을 정리합니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

C++, CTAD, template, C++17, deduction 등으로 검색하시면 이 글이 도움이 됩니다.