auto vs decltype vs decltype(auto) vs decltype((expr))

타입 추론의 함정 - 2019년 2월 23일

카테고리: C++

태그: C++, 모던C++

안녕하세요, static입니다. C++11에 타입 추론을 위한 키워드들이 추가되었는데요(사실 auto는 키워드 재활용이긴 하지만요.). 코드 가독성을 더 높여주고 복잡한 타입을 생략해서 쓸 수 있게 해주는 아주 고마운 기능이지만, 몇가지 함정이 숨어 있습니다. 어제 알게된 사실인데 한번 글로 정리해보려고 합니다.

우선 auto는 prvalue로서의 타입을 얻고자 할 때 사용합니다. prvalue는 우리가 흔히 rvalue라고 알고 있는 것들입니다.

int i = 5;
int& ir = i;
auto a = i; // int
auto b = ir; // int

a, b의 타입은 둘 다 int입니다. ir의 타입이 int&이긴 하나, prvalue로서의 타입을 추론하기 때문에 b 역시 int가 됩니다.

decltype은 특정 식의 타입을 얻고자 할 때 사용합니다.

decltype(5) i = 5; // int
decltype(i + 5) j = 5; // int
decltype(j + 5.) k = 5.; // double

i, j의 타입은 int이며 k의 타입은 double입니다. iint인 이유는 5는 정수 리터럴이기 때문에 int형이기 때문이며, jint인 이유는 int + int = int이기 때문이며, kdouble인 이유는 int + double = double이기 때문입니다.

그러나 decltype은 알고자 하는 타입의 식과 초기화 할 식 이렇게 2가지의 식을 적어주어야 하는 불편함이 있습니다. 위 예제에서 k의 경우 j + 5.5.라는 2개의 식을 사용했는데요. 이런 간단한 코드라면 몰라도 템플릿 들어가고, 규모 커지고 그러면 난리가 날 것입니다. 그래서 decltype(auto)를 사용합니다.

decltype(auto) i = 5; // int
auto& ir = i; // int&
decltype(auto) ir2 = ir; // int&

i의 타입은 int이며 irir2의 타입은 int&입니다. iint인 이유는 5int형이기 때문이며, irint&인 이유는 auto에 의해 int로 타입이 추론되었으나 &가 붙어 있기 때문에 레퍼런스로 취급되었기 때문입니다. 그리고 ir2int&인 이유는 ir의 타입이 int&이기 때문입니다. 이것이 autodecltype(auto)의 차이점입니다. auto는 prvalue로서의 타입을 추론하나, decltype(auto)는 있는 그대로의 타입을 추론합니다.1

int i = 5;
decltype(i) j = 5; // int
decltype((i)) k = j; // int&

이건 decltype의 다소 난해한 점이라고 볼 수도 있는데, 설명을 드리겠습니다. 일단 j는 int형이며 k는 int&형인데, 식에 괄호가 붙었다고 참조가 된다니 의아하실 수도 있을 것입니다. 그러나 C++의 매력은 난해한 듯 하면서도 정갈한 것 아니겠습니까? jk는 같은 규칙에 의해 타입이 추론된 것입니다. decltype(i)i의 타입을 나타냅니다. iint로 선언되었으므로 int로 추론되게 됩니다. 같은 원리로, decltype((i))(i)의 타입을 나타내는데요. 이 값은 lvalue이기 때문에 int&로 추론됩니다. 그러니까, 전자는 ‘변수’ i 자체의 타입을, 후자는 ‘식’ i의 타입으로 추론한 것입니다. 그렇다고 decltype((5))int&&가 되거나 그러지는 않습니다.

마지막으로, auto는 universal reference를 나타낼 때 쓰일 수도 있습니다. universal reference에 대한 자세한 설명은 이 글에서는 하지 않겠습니다.

int i = 0;
auto&& j = i; // int&
auto&& k = 5; // int&&

auto에 레퍼런스를 하나만 붙이면 단순히 lvalue reference이지만 두개를 붙이면 rvalue reference가 아닌 universal reference가 됨에 유의해야 합니다.

그럼 글을 마치겠습니다. 감사합니다.

  1. decltype(auto)뿐만이 아니라 모든 decltype이 그렇습니다.