Item 2: Understand auto type deduction.

auto 타입 추론에 대해 알아보자.


item 1에서 템플릿 타입 추론에 대해 공부했다면, auto 타입 추론에 대한 대부분을 이미 알고 있는 셈이다.

한 가지 희한한 예외를 빼면, auto 타입 추론이 곧 템플릿 타입 추론이기 때문이다.

이는 템플릿 타입 추론과 auto 타입 추론 사이에는 직접적 대응 관계가 존재해, 알고리즘적으로 상호변환이 가능하기 때문이다.

template<typename T>
void f(ParamType param);

f(expr);

위와 같은 일반적인 함수 템플릿과 호출에 대한 타입 추론은, 컴파일러가 expr을 이용해서 T와 ParamType의 타입을 추론해낸다.

auto 타입 추론은 템플릿의 T와 동일한 역할을 하며, 변수의 형식 지정자(type specifier)는 ParamType과 동일한 역할을 한다.

도대체 무슨말일까?? 예제를 보자.

auto x = 27;//type specifier는 그냥 auto 자체이다.

const auto cx = x;//type specifier는 const auto.

const auto& rx = x;//type specifier는 const auto&

위 케이스에서 컴파일러는 마치 다음과 같은 구문이 존재하는 것처럼 행동한다.

즉 : 

template<typename T>
void func_for_x(T param);

func_for_x(27);

template<typename T>
void func_for_cx(const T param);

func_for_cx(x);

template<typename T>
void func_for_rx(const T& param);

func_for_rx(x);

이것과 똑같이 작동한다는 것이다! 한 방에 이해가 되었다!


그에 따라 auto 타입 추론은 템플릿에서의 경우의 세 가지 case와 똑같이 작동한다.(배열, 함수 이름이 포인터로 붕괴하는 방식도 같다.)

하지만 유일하게 다른 점이 있다.

어떤 경우인지 예를 살펴 보자.

//C++98에서는 다음 구문들을 사용할 수 있다.
int x1 = 27;
int x2(27);

//위에 더하여, C++11에서의 균일 초기화(uniform initialization)를 이용하여 다음과 같은 구문도 사용 가능하다.
int x3 = {27};
int x4{27};

위 네 가지 구문 모두 결과적으로 값이 27인 int가 생긴다는 점은 동일하다.

그런데, 고정된 타입 대신 auto를 이용하여 변수를 선언하는 데에는 몇 가지 장점이 있다.(이유는 항목5에 나온다고 함.)

따라서 int를 auto로 대체해 보자.

auto x1 = 27;
auto x2(27);
auto x3 = {27};
auto x4{27};

이 선언들은 모두 문제없이 컴파일되지만, 이전 버전과는 의미가 좀 달라진다.

x3, x4는 int가 아니라 std::initializer_list<int> 타입의 변수를 선언한다(?!).

이유는 다음과 같다.

auto로 선언된 변수의 초기값이 중괄호로 감싸인 형태이면, 추론되는 타입은 std::initializer_list이다.(위 case는 원소가 한개인 경우)

auto x5 = {1,2,3.0f};//따라서 이 코드는 당연히 컴파일이 안된다.

이 경우가 템플릿 타입 추론과 다르게 작동하는 부분은, 같은 템플릿 함수에 동일한 중괄호 초기값을 전달하면 컴파일이 거부된다는 점이다.

auto x = {11,23,9};//당연히 잘됨. 타입은 std::initializer_list<int>

template<typename T>
void f(T param);

f({11,23,9});//컴파일 오류! T에대한 타입추론 불가

정리하면 : auto는 중괄호 초기값이 std::initializer_list를 의미한다고 가정하지만, 템플릿 추론은 그렇지 않다.

이유는 아무도 모른다(책에도 써있다..)

결론 : auto의 초기화를 균일 초기화 할때는 std::initializer_list 타입이라는 것을 염두해 두자.


이상은 C++11까지의 내용이지만, C++14에서는 알아야 할 것이 더 남아있다.

  1. C++14에서는 함수의 리턴 타입을 auto로 지정해서 컴파일러가 추론하게 할 수 있다.
  2. C++14에서는 람다의 파라미터로 auto를 사용할 수 있다.

하지만, 위의 두 용법에서의 auto는 auto 타입 추론이 아니라 item 1에서 배웠던 템플릿 타입 추론의 규칙들이 적용된다.

따라서,

auto createInitList()
{
    return {1,2,3}; //안됨
}

std::vector<int> v;

auto resetV = [&v](const auto& newVal) { v = newVal;};
resetV({1,2,3});//안됨

위의 두 경우는 컴파일이 실패한다.


Things to Remember

  • auto 타입추론은 대체로 템플릿 타입추론과 같지만, auto 타입추론은 중괄호 초기값이 std::initializer_list를 나타내고 템플릿에서는 그렇지 않다.
  • 함수의 리턴값이나 람다 매개변수에 쓰인 auto는 auto가 아니라 템플릿에서의 타입추론이 적용된다.


'C++ > Effective Modern C++' 카테고리의 다른 글

Item 1: Understand template type deduction.  (0) 2016.03.24
Posted by RPG만들기XP
,