← 문제로

48. 클래스 템플릿 인자 추론(CTAD)과 추론 가이드

난이도 상
내 리뷰 · C++
해설 · C++

해설 — 클래스 템플릿 인자 추론(CTAD)과 추론 가이드

난이도: 상

요약

C++17의 CTAD는 std::pair p{1, 2.5} 처럼 클래스 템플릿의 인자를 생성자 호출로부터 추론한다. 컴파일러는 생성자(및 사용자 정의 추론 가이드)로부터 "가상의 함수 템플릿 집합"을 만들어 오버로드 해석하듯 인자를 추론한다. 예측 출력은 true / 2 / 5 / true / 2.

핵심 개념

  • CTAD: 클래스 템플릿 이름만 쓰고(std::vector v{...}) 인자를 생략하면, 컴파일러가 이니셜라이저로부터 템플릿 인자를 추론한다. std::vector<int> v{...} 를 손으로 쓰지 않아도 된다.
  • 추론 가이드(deduction guide): Wrapped(const char*) -> Wrapped<std::string>; 는 "생성자 인자가 const char* 이면 T = std::string 으로 추론하라"고 컴파일러에 알린다. 표준 라이브러리도 이 방식으로 vector(It, It), pair 등의 추론을 정의한다.
  • CTAD는 오브젝트 선언 시점에만 작동한다. 함수 인자, 상속, new 표현식 일부 등에는 제약이 있다.

단계별 내부 동작

  1. 가상 생성자 템플릿 생성: 클래스 템플릿의 각 생성자와 각 추론 가이드로부터 함수 템플릿을 만든다. Wrapped 의 경우:
    • 생성자로부터: template<class T> Wrapped<T> F(T);
    • 가이드로부터: Wrapped<std::string> F(const char*);
  2. 오버로드 해석: 실제 인자로 이 가상 함수들을 오버로드 해석한다. 더 특수한/정확히 맞는 후보가 이긴다.

각 줄:

  • (1) std::pair p{1, 2.5} → pair<int, double>. 2.5 는 double. L1 = true.
  • (2) std::vector v1{5, 5} → vector<int>. 중괄호 목록은 std::initializer_list<int>{5,5} 생성자를 우선 매칭 → 원소가 5, 5 인 크기 2 벡터. L2 = 2.
  • (3) std::vector v2(5, 5) → vector<int>. 괄호는 vector(size_type count, const T& value) 생성자에 매칭 → 값 5가 5개인 크기 5 벡터. L3 = 5.
  • (4)(5) Wrapped w{"hi"} → 추론 가이드가 const char* → Wrapped<std::string> 로 추론. 생성자 후보 Wrapped<const char*> 보다 가이드가 선택된다. L4 = true, w.value 는 std::string("hi") 이므로 .size() = 2. L5 = 2.

흔한 오해·함정

  • {} vs ()의 차이: CTAD 여부와 무관하게, std::vector<int> 에서도 {5,5} 와 (5,5) 는 다른 생성자를 부른다. CTAD가 이 함정을 만든 게 아니라 드러낼 뿐이다. "동일 초기화"라 착각하면 원소 개수가 달라진다.
  • 추론 가이드 없으면: 가이드가 없으면 Wrapped w{"hi"} 는 Wrapped<const char*> 로 추론되어 value.size() 자체가 컴파일 불가. 가이드가 의도를 바로잡는다.
  • 부분 CTAD 불가: std::vector<> v 처럼 일부만 비우는 문법은 없다. 전부 추론하거나 전부 명시.
  • 집계(aggregate) CTAD 는 C++20부터. C++17에서는 사용자 정의 생성자/가이드가 필요.
  • explicit 생성자와 가이드의 explicit 여부가 복사 초기화(Wrapped w = ...) 가능성에 영향을 준다.

면접 포인트

  • CTAD가 "가상 함수 템플릿 오버로드 해석"으로 구현된다는 점과 추론 가이드의 역할.
  • 같은 이니셜라이저라도 {}(initializer_list 우선)와 ()가 다른 생성자를 부르는 이유.
  • 표준 컨테이너들이 반복자쌍·initializer_list 추론을 어떻게 가이드로 제공하는가.