항목 5에서 설명했듯이, auto를 사용해서 변수를 선언하면 형식을 명시적으로 지정했을 때보다 기술적으로 여러 가지 장점이 있다.
그러나 가끔은 auto의 형식 연역이 딴전을 부리기도 한다.
Widget w;
...
bool highPriority = features(w)[5]; // w의 우선순위가 높은가?
...
processWidget(w, highPriority); // w를 그 우선순위에 맞게 처리한다
해당 코드는 잘 작동하지만, 아래 코드는 예측할 수 없다.
auto highPriority = features(w)[5]; // w의 우선순위가 높은가?
processWidget(w, highPriority); // 미정의 행동
auto를 사용하는 버전에서 highPriority의 형식은 더 이상 bool이 아니다.
std::vector<bool>의 operator[]가 돌려주는 것은 std::vector<bool>reference 형식의 객체이다.
std::vector<bool>::reference가 존재하는 것은, bool당 1비트의 압축된 형태로 표현하도록 명시되어 있기 때문이다.
std::vector<T>의 operator[]를 직접적으로 구현할 수 없다. std::vector<T>의 operator[]는 T&를 돌려주도록 되어 있지만, C++에서 비트에 대한 참조는 금지되어 있다.
auto highPriority = features(w)[5]; // highPriority의 형식을 연역한다.
auto에 의해 highPriority의 형식이 연역되기 때문에, std::vecotr<bool>의 5번 비트로 초기화되지 않는다.
어떤 한 구현에서 그 객체는 참조된 비트를 담은 기계어 워드를 가리키는 포인터 하나와 그 워드의 비트들 중 참조된 비트의 위치를 뜻하는 오프셋으로 구성된다.
features 호출은 임시 std::vector<bool> 객체를 돌려준다. 이 임시 객체에는 이름이 없지만, 논의를 위해 temp라고 부르겠다.
temp에 대해 호출된 operator[]는 std::vecotr<bool>::reference 객체를 돌려주며, 그 객체에는 temp가 관리하는 비트들을 담은 자료구조의 한 워드를 가리키는 포인터와 그 워드에서 참조된 비트(5번 비트)에 해당하는 비트의 오프셋이 담겨 있다.
temp는 하나의 임시 객체이므로 문장의 끝에서 파괴된다. 즉, highPriority의 포인터는 댕글링 포인터가 되며, processWidget 호출은 미정의 행동을 유발한다.
보이지 않는 대리자 클래스는 auto와 잘 맞지 않는다.
정리하자면, 다음과 같은 형태의 코드는 피해야 한다.
auto somVar = "보이지 않는" 대리자 클래스 형식의 표현식;
기본적인 설계상의 결정들에 익숙해질수록, 그 라이브러리 안에서 쓰이는 대리자들의 존재 때문에 낭패를 볼 가능성이 줄어든다.
형식 명시 초기치 관용구
초기화 표현식의 형식을 auto가 연역하길 원하는 형식으로 캐스팅 한다.
auto highPriority = static_cast<bool>(features(w)[5]);
이러한 변수 선언 방식은 의도가 표현된다는 장점이 있다.
정리
- "보이지 않는" 대리자 형식 때문에 auto가 초기화 표현식의 형식을 "잘못" 연역할 수 있다.
- 형식 명시 초기치 관용구는 auto가 원하는 형식을 연역하도록 강제한다.
'서적 > Effective Modern C++' 카테고리의 다른 글
항목 8: 0과 NULL보다 nullptr를 선호하라 (0) | 2022.08.18 |
---|---|
항목 7: 객체 생성 시 괄호(())와 중괄호({})를 구분하라 (0) | 2022.07.13 |
항목 5: 명시적 형식 선언보다는 auto를 선호하라 (0) | 2022.07.04 |