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

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

이 글의 핵심

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

들어가며

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

실무에서 마주한 현실

개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.

1. CTAD 기본

C++14 vs C++17

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 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};

표준 라이브러리

아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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

기본 예제

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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
}

복사 생성자

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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)

기본 추론 가이드

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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;
}

커스텀 추론 가이드

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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
}

복잡한 추론 가이드

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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: 모호한 추론

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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: 초기화 리스트

아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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*

아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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 생성자

아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
template<typename T>
class NoDeduction {
public:
    explicit NoDeduction(T value) {}
};
int main() {
    NoDeduction<int> obj(42);
}

6. 실전 예제

예제 1: 스마트 포인터

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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

다음 단계


관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3