Post List

2015년 2월 3일 화요일

item 02: auto의 타입 추론을 이해하자.

작가
Meyers, Scott
출판
O'ReillyMedia
발매
2014.12.12
평점







Effective Modern C++ - Scott Meyers
Item 2: Understand auto type deduction.

item 2. auto의 타입 추론을 이해하자.

* Intro

item 1의 template type deduction을 봤다면, auto type deduction에 대해서 거의 모든걸 이해한 것이다. 단, 한가지 중요한 예외사항만 빼놓는다면 말이다.

template type deduction과 auto type deduction은 직접적으로 mapping이 된다.

이번에도 item 1과 같이 아래의 용어를 쓸것임을 미리 밝혀둔다.

template<typename T>       // T  template에서의 type 의미
void f(ParamType param);   // ParamType T 표현되는 함수정의에 적힌 param type

f(expr);                   // expr 함수 호출시 사용되는 표현

* template type deduction 인듯,
  template type deduction 아닌,
  template type deduction 같은 auto type deduction 

auto 는 T의 역할을 한다.
auto 앞에 붙은 type 지정자와 auto가 합쳐져서 ParamType이 되는 것이고,
= 오른쪽에 오는 것이 expr이 된다.

auto x = 27;
template<typename T>
void func_for_x(T param);

func_for_x(27);
const auto cx = x; template<typename T>
void func_for_cx(const T param);

func_for_cx(27);
const auto& rx = x;
template<typename T>
void func_for_rx(const T& param);

func_for_rx(27);

item 1에서 3가지 경우에 대해서 template type deduction을 살펴보았다.

Case 1 : ParamType이 Reference (&) 이거나 Pointer (*) 인 경우. 단 Universal Reference (&&)는 제외
Case 2 : ParamType이 Universal Reference (&&)인 경우
Case 3 : ParamType이 그냥 Call-by-Value인 경우

위에 이미 case 1, 3에 대해서는 살펴보았다.

auto x = 27;           // case 3
const auto cx = x;     // case 3
const auto& rx = x;    // case 1

case 2에 대한 예제는 아래와 같다.

auto&& uref1 = x;      //  x : lvalue -> uref1 : int&
auto&& uref2 = cx;     // cx : lvalue -> uref2 : const int&
auto&& uref3 = 27;     // 27 : rvalue -> uref3 : int&&

item 1에서 array와 function 에 대해서 decay into pointer가 발생한다고 얘기했는데, auto에서도 역시 똑같다.

const char str[] = "Luna the Star";   // str's type is const char[14]

auto  a1 = str; // const char *a1
auto& a2 = str; // const char (&a2)[14]

void someFunc(intdouble);

auto  f1 = someFunc;  // void (*f1)(int, double);

auto& f2 = someFunc;  // void(&f2)(int, double);

Visual Studio 에서 위와 같이 작성후 a1, a2, f1, f2에 마우스를 살포시 올려 놓으면 친절하게 auto의 type deduction 결과를 보여준다. 그걸 주석으로 적어두었다.

* template type deduction 인듯,
  template type deduction 아닌,
  template type deduction 같은 auto type deduction 

이제부터는 template type deduction과는 다르게 동작하는 auto type deduction에 대해서 살펴볼 것이다.

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


위 4개의 code에 대해서 모두 27로 초기화된 int란 것이라고 알 수 있다.
그럼 int에서 auto로 바꾸면 어떤일이 벌어질까 ?

auto x1 = 27;     // type : int,                        value : 27
auto x2(27);      // type : int,                        value : 27
auto x3 = { 27 }; // type : std::initializer_list<int>, value { 27 }
auto x4{ 27 };    // type : std::initializer_list<int>, value { 27 }

{ } 를 이용한 초기화 (Uniform Initialization)에서는 int가 아닌 std::initializer_list<int>로 초기화가 되었다.

물론 아래와 같은 초기화는 Compile에서부터 error가 발생한다.

auto x5 = { 1, 2, 3.0 }; // error! can't deduce T for std::initializer_list<T>

auto 에는 특별한 type deduction 규칙이 있다. (2단계 추론)

1. Uniform Initialization을 사용할 때는 std::initializer_list<T> 로 추론된다.

2. 그런 다음 T에 대하여 template type deduction을 해야하는데,
   { } 안에 서로 다른 2가지 이상의 type이 있으면 오류가 발생한다.

auto 에는 특별한 type deduction 규칙이 있다. 방금 위에 써놓고 이말을 왜 또 하냐고 ?
template에는 이 특별한 type deduction이 없다. 이말을 하고 싶어서 한 번 더 언급하였다.

auto x = { 1977, 7, 26 }; // std::initializer_list<int>

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

f({ 1977, 7, 26 }); // error C2784: can't deduce template argument for 'T' from initializer-list

template 의 T는 Uniform Initialization을 std::initializer_list<T> 로 추론을 못한다.
이 문제를 해결할려면 ParamType 자체를 바꿔줘야 한다.

template<typename T>
void f(std::initializer_list<T> param);

f({ 1977, 7, 26 }); // T : int, ParamType : std::initializer_list<int>

C++ 14부터는 auto를 함수의 return 에도 사용이 가능하다.
하지만 여기에 사용된 auto는 auto type deduction이 아닌 template type deduction이 적용된 것으로 보인다.

auto LunasBirthday()
{
  return { 1977, 7, 26 }; // error C3108 : can't deduce a type as an initializer-list is not an expression
                          // error C2440 : 'return' : can't convert from 'initializer_list' to 'auto'
}

C++14부터는 Lambda의 parameter에 auto 사용이 가능하다.
하지만 여기에 사용된 auto는 auto type deduction이 아닌 template type deduction이 적용된 것으로 보인다. (어디선가 본 문장 같은데...)

std::vector<int> v;

auto LunasBirthday = [&v](const autod) { v = d};

LunasBirthday({ 1977, 7, 26 });// error : can't deduce a type for initializer-list

아쉽게도 Visual Studio 2015에서는 Lambda의 parameter로 아직 auto가 허용되지 않는다.


Things to Remember

auto type deduction은 거의 template type deduction과 같다. 하지만 auto type deduction에서는 Uniform Initialization을 이용하면 (무조건) std::initializer_list<T>로 초기화가 되는데, template type deduction에서는 오류가 발생한다.
* C++ 14에 추가된 auto의 사용법 중 return type의 auto 와 Lambda의 parameter에 사용된 auto는 auto type deduction이 아닌 template type deduction 규칙을 따른다.