NeuroWhAI의 잡블로그

[C++] CTAD; User-defined deduction guides (사용자 정의 템플릿 인자 추론 가이드) 본문

개발 및 공부/언어

[C++] CTAD; User-defined deduction guides (사용자 정의 템플릿 인자 추론 가이드)

NeuroWhAI 2018. 9. 10. 21:22


https://en.cppreference.com/w/cpp/language/class_template_argument_deduction


"The syntax of a user-defined deduction guide is the syntax of a function declaration with a trailing return type, except that it uses the name of a class template as the function name"

"user-defined deduction guide는 템플릿 클래스를 함수 이름으로 사용한다는 것만 빼면'->'를 사용해 반환형을 명시한 함수 선언 문법입니다."


이름을 좀 더 길지만 알기 쉽게 쓰자면

User-defined template argument deduction guides

== 사용자가 정의하는 템플릿 인자 추론 가이드(지침)


그러니까 원래라면 컴파일러가 자동으로 수행하는 템플릿 인자 추론에 프로그래머가 기교를 넣을 수 있다는 말입니다.


코드를 봅시다.

#include <iostream>
#include <type_traits>

using namespace std;

template <typename T>
class Foo
{
public:
    explicit Foo(const T& a) : data(a)
    { }
    
    Foo(const T& a, const T& b) : data(a + b)
    { }
    
    T data;
};

// 추론 가이드
template <typename T, typename U>
Foo(T, U) -> Foo<std::common_type_t<T, U>>;

int main()
{
    Foo f1{ 42 };
    Foo f2{ 42, 77.7 };
    
    cout << f2.data << endl;
    
    return 0;
}

추론 가이드가 없다고 생각하고 f2를 생성하는 것에 주목해봅시다.

인수로 42, 77.7을 줬는데 각각 int, double로서 다른 타입으로 해석될겁니다.

Foo에 인수를 두개 받는 생성자가 있긴 하지만 타입이 T로 둘 다 같아야 하기 때문에 아래와 같은 오류가 뜰겁니다.

error: class template argument deduction failed 
error: no matching function for call to 'Foo(int, double)' 
... 
하지만 가이드가 있기 때문에 실제론 잘 컴파일 됩니다!
가이드를 뜯어봅시다.
template <typename T, typename U>
Foo(T, U) -> Foo<std::common_type_t<T, U>>;

첫줄은 뭐 그냥 템플릿 파라미터 선언하는 부분이니 넘어가고

Foo(T, U)를 봅시다.

글 처음에 '템플릿 클래스를 함수 이름으로 사용하는...'라고 했으니 가이드 이름은 Foo가 됩니다.

(T, U)는 어떤 의미냐면 '객체 생성시 (T, U)형태의 인수로 생성하려고 하면...'이라는 것으로 보면 됩니다.

그러니까 Foo(T, U)는

'템플릿 클래스 Foo를 (T, U)형태의 인수로 생성하려고 하면...'이 됩니다.

그리고 -> Foo<std::common_type_t<T, U>>;는 직관적으로 '...이 형태를 써라.'가 됩니다.


이 내용을 토대로 이전 코드(Foo f2{ 42, 77.7 };)를 해석하면

'템플릿 클래스 Foo를 (int, double)형태의 인수로 생성하려고 하면 Foo<double> 버전을 사용해라.'가 되겠죠.

(common_type은 여러 타입 중 가장 범위(?)가 넓은 타입을 얻을 수 있는 놈입니다)


사실 아래의 다른 예제 코드 보다가 나온 문법이라 정리 좀 해봤습니다.

#include <iostream>

using namespace std;

template<class... Ts> struct overload : Ts... { using Ts::operator()...; };
template<class... Ts> overload(Ts...) -> overload<Ts...>;

int main()
{
    auto a = overload{
        []() { cout << "()" << endl; },
        [](int) { cout << "(int)" << endl; },
        [](float) { cout << "(float)" << endl; },
    };
    
    a();
    a(42);
    a(42.0f);

    return 0;
}

만약 이 문법이 없었다면 위 코드를 아래처럼 구현해야 했겠죠.

template<class... Ts> struct overload : Ts...
{
    overload(Ts&&...) { }
    using Ts::operator()...;
};

전혀 쓸모 없는, 오직 꼼수만을 위한 생성자를 추가해줘야 합니다.


이렇게 말하니 이 문법이 생성자를 추가해주는 문법이라는 느낌이 들기도 하네요.




Comments