물론 사용 방법은 다르지만 템플릿 타입 추론과 auto 타입 추론은 직접적으로 연결이 되어있다.
말 그대로 변형 알고리즘을 이용하여 하나에서 다른 하나로 바꾸는 것이다.
auto x = 27; const auto cx = x; const auto& rx = x;
item1의 템플릿의 param의 타입과 굉장히 흡사하지 않는가? 여기서 타입을 결정하는 것은 auto와 그 주변에 붙어있는 것들이다.
컴파일러는 위와 같은 코드를 마치 템플릿이 사용된 것 처럼 이해한다. 다시 말해 아래와 같은 코드로 바꿀 수 있다는 말이다.
templatevoid func_for_x(T param); func_for_x(27); template void func_for_cx(const T param); func_for_cx(x); template void func_for_rx(const T& param); func_for_rx(x);
item 1에서 ParamType에 따라서 3 가지 케이스로 나누어 타입 추론을 이해한 것 처럼 auto에도 동일한 규칙이 적용된다.
케이스 1: 타입 구분자(auto)가 포인터나 레퍼런스이고 유니버셜 레퍼런스가 아닐 때.
케이스 2: 타입 구분자가 유니버셜 레퍼런스 일 때.
케이스 3: 타입 구분자가 포인터나 레퍼런스가 아닐 때.
item 1에서 이야기 한 배열이나 함수 인자의 경우 추론에 사용되었던 규칙들 또한 auto에서 동일하게 적용된다.
"더 이상의 자세한 설명은 생략한다."
하지만 이제 한 가지 예외가 등장한다.
C++98 은 여러분에게 int를 선언하는 두 가지 방법을 제공하였다. 그리고 C++11은 두 가지 방법을 추가한다.
int x1 = 27; int x2 =(27); int x3 = {27}; int x4{ 27 };
네 가지 다른 선언이 존재하지만 결과는 같다. 값이 27인 int를 선언하는 것이다.
이 때 auto를 사용하면 편리하고 좋지 않을까? 그래서 아래와 같이 선언해보았다.
auto x1 = 27; auto x2 =(27); auto x3 = {27}; auto x4{ 27 };
처음 두 개는 정상적으로 동작한다.(기대한대로)
하지만 뒤의 두 개는 int가 아니라 27이라는 값을 가지고 있는 std::initializer_list<int> 라는 타입으로 추론된다.
이것은 auto의 특별한 타입 추론 규칙 때문이다. 만약 auto로 선언된 변수가 중괄호{}로 감싸져있다면, 추론 타입은 std::initializer_list가 된다.
만약 위의 타입으로 추론될 수 없는 경우라면 에러가 난다!
auto x5 = { 1, 2, 5.0 } // 이건 에러다.
이것이 auto와 템플릿 타입 추론의 유일한 차이점이다.
auto x = { 11, 25, 9 } // 이건 정상 동작한다. std::initilizer_list<int> 가 될 것이다. template<typename t=""> void f(T param); f({ 11, 25, 9 }); // 이건 에러다. auto와 동일한 선언이지만 템플릿에서는 타입이 추론될 수 없다. void f(std::initializer_list<t> initList); // 이처럼 직접 선언해주면 정상 동작한다.
C++11에서 의도하지 않게 std::initializer_list 타입의 변수가 선언되는 실수가 종종 발생한다. {}를 쓸 때는 유의하자.
하지만! C++14에서는 또 하나가 추가된다 ㅠㅠ 그지같은..
함수의 반환 타입을 auto로 선언할 수 있는데, 이 때 사용되는 추론규칙은 auto의 그것이 아니라 템플릿의 그것이다.
따라서 아래와 같은 코드는 에러를 내뿜는다.
auto createInitList() { return { 1, 2, 3 }; // 에러.. 추론안됨! } auto가 람다의 인자값으로 쓰였을 때에도 같은 에러가 발생할 수 있다. std::vector<int> v; ... auto resetV = [&v](const auto& newValue) { v = newValue }; ... resetV({ 1, 2, 3 }); //삐... 자동으로 추론되지 않는다.
----------
핵심요약
1. auto 타입 추론은 template의 타입 추론과 보통 같다고 볼 수 있다. 하지만 {}를 사용해 선언할 때에는 std::initilizer_list 로 추론되므로 유의하자.
2. 함수의 반환값이나 람다의 인자로 auto가 사용된 경우 auto가 아니라 템플릿의 추론규칙을 사용한다.