C++ 템플릿 함수 반환형 추론(decltype, auto)

프로그래밍/C++ 2019.08.26 댓글 Plorence

타입이 무엇인가?

프로그래머가 템플릿 함수를 쓰려고 할 때 한 가지 문제점은, C++98에서 선언을 할 때에 어떤 타입을 사용해야 하는지 알아내는 방법이 항상 가능한 것은 아니라는 점입니다.

template<typename T1,typename T2>
void Add(T1 a, T2 b) {
       ? result a + b;
}

이때 result의 타입은 뭐가 되어야 하는지 알 수 없습니다.
T1, T2의 타입들은 모든 타입이 올 수 있으므로 덧셈 연산으로 인해 뭐를 반환할지 알 수 없는 셈입니다.

 

예시 상황

#include <iostream>
class Integer {
private:
       int n;
public:
       Integer(int n = 0) {
              this->n = n;
       }
       Integer operator+(int n) {
              Integer result(this->n + n);
              return result;
       }
       int GetNumber() {
              return n;
       }
};
template<typename T1,typename T2>
?? Add(T1 a, T2 b) {
        return a + b;
}
int main(void) {
       Integer number(10);
       Integer result = Add(number,10);
       std::cout << result.GetNumber();
}

위와 같은 코드에 대해 고민할 수 있습니다.
객체와 int형의 덧셈 연산으로 인해 객체가 올지, int형이 올지, 아니면 다른 타입이 올지 알 수 없습니다.

 

해결 하기

template<typename T1,typename T2>
decltype(a+b) Plus(T1 a, T2 b) {
}

그럼 이렇게 사용하면 될 것 같지만, 컴파일 에러가 발생합니다.
왜냐하면 사전에 미리 알지 못한 시점에서 이 코드에 있는 x와 y 매개체를 선언하지 못했기 때문입니다.
그래서 decltype 키워드는 매개변수가 선언된 이후에 와야 하는데, 이를 위해서 C++11은 함수를 선언하고 정의하는 새로운 구문을 허용합니다.

template<typename T1,typename T2>
auto Add(T1 a, T2 b) -> decltype(a+b) {
        return a + b;
}

이런 방식은 후행 반환 형식(trailing return type)으로 불립니다.
따라서 auto는 또 다른 새로운 C++11 역할로서 후행 반환 형식에 의해서 제공받는 타입에 의해서 동일한 형태가 함수의 정의에 사용될 수 있습니다.

 

C++14

C++14에서는 후행 반환 형식 없이 반환형에 auto만 써도 됩니다.

template<typename T1,typename T2>
auto Add(T1 a, T2 b) -> decltype(a+b) {
        return a + b;
}

컴파일 에러는 없지만 프로그래머의 의도와 다르게 반환 형식이 결정될 수 있는 문제가 있을 수 있습니다.
위에서 반환되는 auto 타입은 템플릿 타입 추론에 의해 결정됩니다.

이를 방지하려면 decltype의 표현식으로 auto키워드를 쓰면 됩니다.

#include <iostream>
template<typename T1,typename T2>
decltype(auto) Plus(T1 a, T2 b) {
    return a + b;
}
int main(void) {
    std::cout << Plus(1, 1);
}

이 방식을 사용한다면 후행 반환 형식을 사용하지 않아도 됩니다.

 

구체적인 동작 방식은 이펙티브 모던 C++(보라색)에 설명되어 있습니다.

댓글