티스토리 뷰

 

Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14
Meyers, Scott

 

Item 2 : Understand auto type deduction

 

auto 타입 추론 = 템플릿 타입 추론 (Direct Mapping)


template<typename T

void f(ParamType param);

f(expr);                    // call f with some expression 


auto x = 27; 

const auto cx = x; 

const auto& rx = x


auto = 템플릿의 T역할, 타입 지정자 = ParamType 역할


template<typename T>                     // conceptual template for 

void func_for_x(T param);                 // deducing x's type

func_for_x(27);                                // conceptual call: param's 

                                                    // deduced type is x's type


template<typename T>                     // conceptual template for 

void func_for_cx(const T param);    // deducing cx's type

func_for_cx(x);                               // conceptual call: param's                                   

                                                   // deduced type is cx's type


template<typename T>                    // conceptual template for 

void func_for_rx(const T& param);  // deducing rx's type

func_for_rx(x);                              // conceptual call: param's                                   

                                                  // deduced type is rx's type

 

Item 1에서 템플릿 타입 추론을 세 경우로 나눠서 살펴봤음.

- Case 1 : 타입 지정자가 포인터 또는 레퍼런스인 경우

- Case 2 : 타입 지정자가 유니버셜 레퍼런스인 경우

- Case 3 : 타입 지정자가 포인터 또는 레퍼런스가 아닌 경우

 

auto x = 27;             // case 3 (x is neither ptr nor reference) 

const auto cx = x;    // case 3 (cx isn't either)

const auto& rx = x;   // case 1 (rx is a non-universal ref.)

 

auto&& uref1 = x;     // x is int and lvalue, so uref1's type is int& 

auto&& uref2 = cx;    // cx is const int and lvalue, so uref2's type is const int&

auto&& uref3 = 27;    // 27 is int and rvalue, so uref3's type is int&

 

이처럼 auto 타입 추론 = 템플릿 타입 추론. 단 한 가지 예외 사항이 있음.

- 초기값 27을 가지는 int 변수를 선언하고 싶다고 가정.

- C++ 98에서는 두 가지 문법을 제공했었음.

int x1 = 27;

int x2(27);

 

- C++ 11에서는 유니폼 초기화(Uniform Initalization)을 지원함.

int x3 = {27};

int x4{27};

 

앞에서 x1, x2, x3, x4의 타입은 전부 int.

하지만 auto로 바꾸면 사정이 달라짐.

auto x1 = 27;       // type is int, value is 27

auto x2(27);        // ditto

auto x3 = { 27 };  // type is std::initializer_list<int>, value is { 27 }

auto x4{ 27 };     // ditto

 

auto에는 특별한 타입 추론 규칙이 있음.

- auto로 선언된 변수에 유니폼 초기화를 사용할 때, 추론된 타입은 std::initializer_list가 됨.

- 만약 추론할 수 없는 타입이면, 오류가 발생함.

 auto x5 = {1, 2, 3.0}         // 서로 다른 타입, std::initializer_list<T>로 추론할 수 없음

 

실제로 두 종류의 타입 추론이 일어남.

1.  auto의 사용으로 인해 x5의 타입이 std::initializer_list로 추론됨.

2. std::initializer_list는 템플릿이므로  타입 T에 대해 std::initializer_list<T>로 인스턴스화가 일어남. 즉, T의 타입에 대해서도 추론이 일어남.

auto 타입 추론과 템플릿 타입 추론의 차이{}에 대해 auto타입 추론은 std::initializer_list로 추론하고, 템플릿 타입 추론은 그렇지 않다는 것!

auto x = {11, 23, 9}        // x의 타입은 std::initializer_list<int>

template<typename T>      // 매개변수가 있는 템플릿의 선언은 x의 선언와 동일함.

void f(T param);              

f({11, 23, 9});                // 오류! T의 타입을 추론할 수 없음.

 

하지만 템플릿의 매개변수를 std::initializer_list<T>로 지정하면, 템플릿 타입 추론은 T가 무엇인지 추론할 수 있음.

template<typename T>

void f(std::initializer_list<T> initList);

f({11,23, 9});                  // T는 int로 추론

                                      // initList의 타입은 std::initializer_list<int>

 

C++14에서는 함수의 리턴 타입에 auto를 사용해 추론 가능.

- auto 타입 추론이 아닌 템플릿 타입 추론을 이용함.

auto createInitList()

  return { 1, 2, 3 };       // error: can't deduce type
}                                 // for { 1, 2, 3 }

 

C++14에서는 람다식에서 매개변수 선언 시 auto 사용 가능.

- auto 타입 추론이 아닌 템플릿 타입 추론을 이용함.

std::vector<int> v;

auto resetV =  [&v](const auto& newValue) { v = newValue; };     // C++14

resetV({ 1, 2, 3 });          // error! can't deduce type for { 1, 2, 3 }

 

 Summary

  • 일반적으로 auto 타입 추론은 템플릿 타입 추론과 같다. 하지만 auto 타입 추론은 유니폼 초기화를 사용한 경우 std::initializer_list를 나타낸다고 가정하지만, 템플릿 타입 추론은 그렇지 않다.
  • 함수의 리턴 타입이나 람다식의 매개변수로 사용하는 auto는 auto 타입 추론이 아닌 템플릿 타입 추론을 암시한다.

 

 

 

 

 

 

- James Song

댓글