[C++20 새기능 소개] 클래스 템플릿의 인자 추론 (Class Template Argument Deduction, CTAD)

C++20에서는 클래스 템플릿의 인자 추론(Class Template Argument Deduction, CTAD)이 더욱 강화되어, 클래스 템플릿을 사용할 때 템플릿 인자를 명시적으로 지정하지 않아도 컴파일러가 자동으로 추론할 수 있게 되었습니다. 이번 글에서는 CTAD의 개념과 사용법, 그리고 이전 버전과 비교하여 어떻게 개선되었는지 알아보겠습니다.

클래스 템플릿 인자 추론이란 무엇인가요?

클래스 템플릿 인자 추론(Class Template Argument Deduction, CTAD)은 클래스 템플릿을 인스턴스화할 때 템플릿 인자를 명시적으로 지정하지 않아도, 컴파일러가 생성자의 인자 등을 기반으로 템플릿 인자를 자동으로 추론하는 기능입니다. 이는 C++17에서 도입되었지만, C++20에서는 사용자 정의 클래스에서도 추론 가이드(Deduction Guide)를 통해 더욱 유연하게 활용할 수 있게 되었습니다.

이전 버전에서는 어떻게 했나요?

C++17 이전에는 클래스 템플릿을 인스턴스화할 때 항상 템플릿 인자를 명시적으로 지정해야 했습니다.

예제: 기존 방식의 클래스 템플릿 사용

template <typename T>
class Wrapper {
public:
    Wrapper(T value) : value_(value) {}
    T get() const { return value_; }
private:
    T value_;
};

int main() {
    Wrapper<int> w(42); // 템플릿 인자 <int>를 명시적으로 지정
    std::cout << w.get() << "\n"; // 출력: 42
    return 0;
}
  • 문제점:
    • 클래스 템플릿을 사용할 때마다 템플릿 인자를 명시적으로 지정해야 하므로 코드가 장황해집니다.
    • 특히 생성자의 인자로부터 타입을 유추할 수 있음에도 불구하고, 이를 활용하지 못했습니다.

C++20의 클래스 템플릿 인자 추론을 사용한 개선

C++20에서는 컴파일러가 생성자의 인자 등을 기반으로 템플릿 인자를 자동으로 추론할 수 있습니다. 또한 사용자 정의 클래스에서도 추론 가이드를 작성하여 원하는 방식으로 템플릿 인자를 추론하도록 할 수 있습니다.

예제: 클래스 템플릿 인자 추론 사용

template <typename T>
class Wrapper {
public:
    Wrapper(T value) : value_(value) {}
    T get() const { return value_; }
private:
    T value_;
};

int main() {
    Wrapper w(42); // 템플릿 인자 <int>를 추론
    std::cout << w.get() << "\n"; // 출력: 42
    return 0;
}
  • Wrapper<int> 대신 Wrapper로 간단하게 선언할 수 있습니다.
  • 컴파일러는 생성자의 인자 42를 보고 T가 int임을 추론합니다.

어떻게 좋아졌나요?

  • 코드 간결화: 템플릿 인자를 명시적으로 지정할 필요가 없어 코드가 깔끔해집니다.
  • 유연성 향상: 생성자의 인자 타입에 따라 템플릿 인자를 자동으로 추론하므로, 다양한 타입에 대해 코드의 재사용성이 높아집니다.
  • 가독성 개선: 불필요한 템플릿 인자 표기를 줄여 코드의 가독성이 향상됩니다.

상세한 예제와 비교

1. 표준 컨테이너와 CTAD

C++17부터 표준 컨테이너에서도 CTAD가 지원되어, 템플릿 인자를 생략할 수 있었습니다.

#include <vector>

int main() {
    std::vector vec = {1, 2, 3}; // std::vector<int>로 추론
    return 0;
}

2. 사용자 정의 클래스에서의 추론 가이드

C++20에서는 사용자 정의 클래스에서도 추론 가이드를 통해 CTAD를 커스터마이징할 수 있습니다.

예제: 추론 가이드 사용

template <typename T>
class Pair {
public:
    Pair(T first, T second) : first_(first), second_(second) {}
    T first() const { return first_; }
    T second() const { return second_; }
private:
    T first_;
    T second_;
};

// 추론 가이드 작성
template <typename T1, typename T2>
Pair(T1, T2) -> Pair<std::common_type_t<T1, T2>>;

int main() {
    Pair p(3, 4.5); // T를 double로 추론
    std::cout << p.first() << ", " << p.second() << "\n"; // 출력: 3, 4.5
    return 0;
}
  • 추론 가이드를 통해 Pair 클래스의 템플릿 인자를 T1과 T2의 공통 타입으로 추론하도록 지정했습니다.
  • 따라서 int와 double을 인자로 전달하면 T는 double로 추론됩니다.

3. 복잡한 생성자와 CTAD

template <typename T>
class Container {
public:
    Container(std::initializer_list<T> list) : data_(list) {}
    // 다른 생성자들...
private:
    std::vector<T> data_;
};

// 추론 가이드 작성
template <typename T>
Container(std::initializer_list<T>) -> Container<T>;

int main() {
    Container c{1, 2, 3}; // T를 int로 추론
    return 0;
}
  • std::initializer_list를 인자로 받는 생성자를 사용하여 템플릿 인자를 추론합니다.
  • 추론 가이드를 통해 정확한 타입을 추론할 수 있습니다.

주의 사항

명확한 타입 추론 필요: 컴파일러가 템플릿 인자를 추론할 수 있어야 합니다. 모호한 경우에는 추론이 실패할 수 있습니다.

Wrapper w1(42); // T를 int로 추론
Wrapper w2(3.14); // T를 double로 추론
Wrapper w3("Hello"); // T를 const char*로 추론
Wrapper w4(); // 오류: 생성자 인자가 없으므로 추론 불가

 

추론 가이드의 정확성: 잘못된 추론 가이드를 작성하면 예상치 못한 타입으로 추론될 수 있습니다.

// 잘못된 추론 가이드
template <typename T> Pair(T, T) -> Pair<int>;

Pair p(3, 4.5); // T를 int로 추론하므로 데이터 손실 발생

 

복잡한 생성자: 생성자가 오버로딩되어 있거나, 인자가 복잡한 경우에는 추론 가이드가 필수적입니다.

요약

C++20의 클래스 템플릿 인자 추론은 이전 버전에서 불편했던 템플릿 인자 명시를 생략하고, 컴파일러가 자동으로 타입을 추론할 수 있게 해줍니다. 이를 통해 코드의 가독성과 유연성이 향상되며, 개발자의 코딩 시간을 절약할 수 있습니다. 특히 사용자 정의 클래스에서도 추론 가이드를 활용하여 원하는 방식으로 타입을 추론할 수 있어, 템플릿 프로그래밍이 더욱 편리해졌습니다.

 

참고 자료:

반응형