KoreanFoodie's Study

Effective Modern C++ | 항목 6 : auto 가 원치 않은 형식으로 연역될 때는 명시적 형식의 초기치를 사용하라 본문

Tutorials/C++ : Advanced

Effective Modern C++ | 항목 6 : auto 가 원치 않은 형식으로 연역될 때는 명시적 형식의 초기치를 사용하라

GoldGiver 2022. 10. 26. 09:50

C++ 프로그래머의 필독서이자 바이블인, 스콧 마이어스의 Modern Effective C++ 를 읽고 기억할 내용을 요약하고 있습니다. 꼭 읽어보시길 추천드립니다!

항목 6 : auto 가 원치 않은 형식으로 연역될 때는 명시적 형식의 초기치를 사용하라

핵심 :

1. "보이지 않는" 대리자 형식 때문에 auto 가 초기화 표현식의 형식을 "잘못" 연역할 수 있다.
2. 형식 명시 초기치 관용구는 auto 가 원하는 형식을 연역하도록 강제한다.


다음 예시를 보자.

class Widget {};

std::vector<bool> feature(const Widget& w)
{
	return std::vector<bool>(10);
}

void processWidget(Widget w, bool highPriority) { /* Do something */ }

int main()
{
	Widget w;
	bool highPriority = feature(w)[5];
	processWidget(w, highPriority);

	auto highPriority2 = feature(w)[5];
	// 미정의 동작
	processWidget(w, highPriority2);
}

std::vector<bool> 의 operator [ ] 는 요소에 대한 참조(bool 을 제외한 모든 형식에 대해서는 std::vector::operator[ ] 가 참조를 돌려줌)가 아닌, std::vector<bool>::reference 형식의 객체를 돌려준다. 그 이유는, std::vector<bool> 이 자신의 bool 들을 bool 당 1 비트의 압축된 형태로 표현하도록 명시되어 있기 때문이다. C++ 는 비트에 대한 참조는 금지되어 있어서, std::vector<bool>::reference 를 bool 로 암묵적으로 변환하는 방식을 채택한다.
operator [ ] 가 std::vector<bool>::reference 를 반환하는데, 이는 보통 참조된 비트를 담은 기계어 워드(word) 를 가리키는 포인터 하나와 오프셋으로 구성되어 있다. 명시적으로 bool 로 선언한 경우, 해당 오프셋을 읽은 후 그 값이 바로 bool 타입으로 변환되어 저장된다. 그런데 auto 는 지금 포인터와 오프셋을 담은 객체로 연역된 상태이다. 추가로 bool 로 암시적 변환이 되는 게 아니란 뜻이다.
그런데 feature 함수는 std::vector<bool> 타입의 임시 객체(temp 라고 부르겠다)를 돌려준다. 그런데 temp 는 임시 객체이므로 문장의 끝에서 파괴된다. 따라서 highPriority 의 포인터는 대상을 잃은 포인터(dangling pointer) 가 되어 processWidget(w, highPriority2) 는 미정의 동작을 보이는 것이다.
std::vector<bool>::reference 는 대리자 클래스(proxy class), 즉 다른 어떤 형식의 행동을 흉내 내고 보강하는 것이 존재 이유인 클래스의 예이다. 그리고 대체로 이러한 "보이지 않는" 대리자 클래스는 auto 와 잘 맞지 않는 경향이 있다! 따라서, 다음과 같은 형태의 코드는 피해야 한다.

auto someVar = "보이지 않는" 대리자 클래스 형식의 표현식;

// "보이지 않는" 대리자 클래스 형식의 표현식 구현 예시
// std::vector<bool>::operator[ ] 의 명세
namespace std
{
	template<class Allocator>
	class vector<bool, Allocator>
	{
	public:
		class reference { ... };

		reference operator[](size_type n);
		...
	};
}


하지만 대리자 클래스를 쓰는 라이브러리를 사용한다고 해서 auto 를 아예 안 써야 하는 건 아니다. 다음과 같이 형식 명시 초기치 관용구(explicitly typed initializer idiom)를 사용하면 된다.

auto highPriority2 = static_cast<bool>(feature(w)[5]);

 

Comments