스토리텔링 개발자

[Effective Modern C++] 2. auto 타입 추론 규칙 본문

개발/Effective Modern C++

[Effective Modern C++] 2. auto 타입 추론 규칙

김디트 2025. 2. 6. 11:13
728x90

항목 2. auto의 형식 연역 규칙을 숙지하라

 

 

 

auto와 template의 타입 추론
  • 한 가지 기이한 예외를 제외하면, auto 타입 추론이 곧 템플릿 타입 추론이다.
// 템플릿 타입 추론
// expr과 ParamType의 타입을 통해 T를 추론한다.
template<typename T>
void f(ParamType param);
f(expr); // 함수 실행


// auto 타입 추론
// auto : T와 동일한 역할
// 변수의 타입 지정자(type specifier) : ParamType과 동일한 역할
// 각각 아래의 주석 코드의 템플릿 타입 추론과 동일하다.
auto x = 27; // 타입 지정자 : auto
// template<typename T>
// void func_for_x(T param);
// func_for_x(27);
const auto cx = x; // 타입 지정자 : const auto
// template<typename T>
// void func_for_cx(const T param);
// func_for_cx(x);
const auto& rx = x; // 타입 지정자 : const auto&
// template<typename T>
// void func_for_rx(const T& param);
// func_for_rx(x);
  • 그러므로 역시 세 가지 경우로 나뉜다.
    1. 타입 지정자가 포인터나 참조 타입이지만, 보편 참조(universal reference)는 아닌 경우
    2. 타입 지정자가 보편 참조인 경우
    3. 타입 지정자가 포인터도 참조도 아닌 경우
auto x = 27; // 경우 3
const auto cx = x; // 경우 3
const auto& rx = x; // 경우 1

auto&& uref1 = x; // 경우 2, 타입은 int&
auto&& uref2 = cx; // 경우 2, 타입은 const int&
auto&& uref3 = 27; // 경우 2, 타입은 int&&
  • 배열과 함수 이름이 포인터로 붕괴되는 것 역시 동일하다.
const char name[] = "R. N. Briggs";
auto arr1 = name; // 타입은 const char*
auto& arr2 = name; // 타입은 const char (&)[13]

void someFunc(int, double);
auto func1 = someFunc; // 타입은 void (*)(int, double)
auto& func2 = someFunc; // 타입은 void (&)(int, double)

 

 

 

템플릿과 다른 한 가지 예외
  • 초기값을 사용하여 int 변수를 선언하는 예
// C++98
int x1 = 27;
int x2(27);
// C++11
// 균일 초기화(uniform initialization) 지원
int x3 = { 27 };
int x4{ 27 };
  • 하지만 고정된 형식 대신 auto를 사용하면 몇 가지 장점이 있으므로 auto로 변경 해보자.(항목 5 참조)
    • 이 경우, 컴파일은 되지만, 의미가 달라진 것들이 생긴다!
auto x1 = 27; // 동일. 타입은 int, 값은 27
auto x2(27); // 동일. 타입은 int, 값은 27
auto x3 = { 27 }; // 변경됨. 타입은 std::initializer_list<int>, 값은 { 27 }
auto x4{ 27 }; // 변경됨. 타입은 std::initializer_list<int>, 값은 { 27 }
  • auto의 특별한 타입 추론 규칙
    • auto로 선언된 변수의 초기값(initializer)이 중괄호 쌍으로 감싸인 형태라면, std::initializer_list로 추론된다.
    • 이 추론이 불가능할 경우 컴파일 에러가 발생한다.
auto x5 = { 1, 2, 3.0 } // T를 추론할 수 없으므로 컴파일 에러
  • 사실 auto로 std::initializer_list를 추론하는 경우 두 가지 타입을 추론하게 된다.
    1. auto에 의한 타입 추론
    2. 추론된 타입인 std::initializer_list가 템플릿이므로 템플릿 타입 추론
  • 즉, 위의 컴파일 에러의 경우 두 번째 타입 추론을 실패한 것이다.
  • 이 타입 추론은 auto에서만 유효하다.
auto x = { 11, 23, 9 }; // std::initializer_list<int>로 타입 추론 성공

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

f({ 11, 23, 9 }); // 타입을 추론할 수 없어서 컴파일 에러

// 아래와 같이 바꾸어주면 된다.

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

f({ 11, 23, 9 }); // std::initializer_list<int>로 타입 추론 성공
  • 즉, 중괄호 초기값을 만나면 auto의 경우 std::initizlier_list라고 추론하지만, 템플릿은 그렇지 않다.

 

 

C++14에서의 auto 타입 추론
  • auto는 함수 리턴 타입에도 사용할 수 있다.(항목 3 참조)
  • 하지만, 이 경우 템플릿 타입 추론 규칙이 적용된다.
auto createInitList()
{
    return { 1, 2, 3 }; // 타입 추론 불가능으로 컴파일 오류!
}
  • 람다 매개변수로 auto가 사용될 때도 마찬가지 이유로 컴파일 에러가 발생한다.
std::vector<int> v;
...
auto resetV = [&v](const auto& newValue) { v = newValue; };
...
resetV({ 1, 2, 3 }); // 타입 추론 불가능으로 컴파일 오류!

 

728x90
Comments