Item 6. auto가 원치 않는 타입으로 추론될 때에는 명시적 타입의 초기화를 사용해라


auto가 원치 않는 타입으로 추론되는 경우

auto가 내가 생각한 타입과 다른 타입으로 추론되는 경우들이 존재한다.

간단한 예시로 std::vector<bool> 컨테이너에서 [](인덱스 연산자)를 사용하는 경우가 있다.

std::vector<bool> v = {true, false, true};

auto b = v[2]; // b의 타입은 bool이 아닌 std::vector<bool>::reference 이다.

std::vector<T>operator[]T&를 반환한다고 생각해서 autobool 타입을 추론할 것이라 기대했다면 상당히 당황스러울 수 있다.

auto가 추론한 타입이 bool이 아니라 std::vector<bool>::reference인 이유는 std::vector<bool> 컨테이너가 내부에서 bool을 1bit의 압축된 형태로 표현하기 때문이다.

C++에서는 비트에 대한 참조는 금지되어 있어 bool&로 반환할 수 없기에 마치 bool&처럼 동작하는 std::vector<bool>::reference를 사용한다.

std::vector<bool>::referencebool 타입으로 암시적 변환이 가능하다. 즉, 타입을 명시해 줄 경우 우리가 의도한 타입으로 사용할 수 있다.

std::vector<bool> features(const Widget& w);

Widget w;

// std::vector<bool>::reference에서 bool로 암시적 변환
bool highPriority = features(w)[5]

processWidget(w, highPriority);

그러면 원치 않는 타입으로 추론된 auto를 사용할 경우 어떤 문제가 있을까?

// highPriority가 std::vector<bool>::reference 타입으로 추론
auto highPriority = features(w)[5]

// hightPriority는 파괴된 메모리를 가리키는 허상 포인터(dangling pointer)임
processWidget(w, highPriority);

std::vector<bool>::referencestd::vector<bool> 컨테이너 내부의 bool 데이터를 가리키고 있는 객체이다. (bit pointer와 offset을 가지고 있음)

만약 features(w)[5]와 같이 문장 끝에서 파괴되는 임시 객체를 가리키고 있을 경우, 다음 문장에서 std::vector<bool>::reference는 파괴된 메모리를 가리키는 허상 포인터(dangling pointer)가 된다.


auto와 대리자 클래스(Proxy Class)의 관계

std::vector<bool>::reference처럼 특정한 타입의 행동을 흉내 내고 보강하는 클래스를 대리자 클래스(proxy class)라고 불린다.

proxy class는 여러 목적에 맞게 사용된다.

비트에 대한 참조를 제공하는 std::vector<bool>::reference, 포인터의 기능을 보강하는 스마트 포인터 등등

이러한 proxy class들이 외부에서 잘 보여지지 않는 구현일 경우 auto와 만났을 때 원치 않는 타입으로 추론되는 문제를 일으킨다.

C++ 라이브러리에서는 표현식 템플릿(expression template) 기법을 사용하는 proxy class들이 많은데 여기서도 추론 문제가 발생할 수 있다.

// Matrix 타입
Matrix sum = m1 + m2 + m3 + m4;

// Sum<Sum<Sum<Matrix, Matrix>, Matrix>, Matrix> 타입
auto sum = m1 + m2 + m3 + m4;

우변의 표현식을 효율적으로 계산하기 위해서 operator+Matrix 객체 타입을 반환하지 않고 Sum<Matrix, Matrix>와 같은 proxy class 타입을 반환한다.

어차피 계산이 끝나면 Matrix 타입으로 초기화할 수 있도록 암시적 형변환이 일어나지만 만약 변수 타입을 auto로 지정할 경우 Sum<Sum<Sum<Matrix, Matrix>, Matrix>, Matrix> 타입으로 추론이 되어 버린다.

즉, auto보이지 않는 proxy class 타입의 표현식을 추론할 때 문제가 발생한다.

이런 문제를 해결하기 위해서 명시적 타입의 초기화를 사용하는 방법이 있다.


auto를 추론하기 위해 명시적 타입의 초기화를 사용하는 방법

명시적 타입의 초기화(explicitly typed initializer idiom)는 명시적 형변환 연산자인 static_cast를 사용해서 auto가 원하는 타입으로 추론하도록 강제하는 것이다.

이 방법을 사용하면 앞서 auto가 원치 않는 타입으로 추론되는 것을 해결할 수 있다.

std::vector<bool> features(const Widget& w);

Widget w;

// std::vector<bool>::reference 타입에서 bool 타입으로 명시적 변환
auto highPriority = static_cast<bool>(features(w)[5]);

// Sum<Sum<Sum<Matrix, Matrix>, Matrix>, Matrix> 타입에서 Matrix 타입으로 명시적 변환
auto sum = static_cast<Matrix>(m1 + m2 + m3 + m4);

static_cast를 사용하면 의도하고자 하는 타입을 코드에서 명확하게 표현할 수 있는 장점도 있다.


카테고리:

업데이트: