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

게시:     수정

카테고리:

태그: ,

이 글은 아래의 책을 정리하였습니다. 이펙티브 모던 C++, 스콧 마이어스 저자, 류광 번역

📦 2. auto

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

⚠️ 문제 상황

// 반환 값의 각 bool은 특정 기능 지원 여부를 뜻한다.
vector<bool> features(const Widget& w);

Widget w;
bool highPriority = features(w)[5];
processWidget(w, highPriority);
  • bool로 명시하면 정상 동작한다.
// 반환 값의 각 bool은 특정 기능 지원 여부를 뜻한다.
vector<bool> features(const Widget& w);

Widget w;
auto highPriority = features(w)[5];

// ⚠️ 컴파일은 되지만, 미정의 동작!
processWidget(w, highPriority);
  • auto로 바꾸면 컴파일은 된다.
  • 하지만, 원치 않는 형식으로 연역되어 미정의 동작이 수행된다.
    • bool로 연역되지 않는다!

🔎 문제 이유 1: vector<bool>의 특이성

  • vector<bool>은 메모리 효율을 위해 각 bool1비트로 압축 저장
  • C++은 비트에 대한 참조가 금지되어, vector<T>::operator[]T&을 돌려줄 수 없음
  • 우회책으로 bool&처럼 동작하는 프록시 객체 vector<bool>::reference를 반환

🔎 문제 이유 2: 댕글링 포인터

auto highPriority = features(w)[5];
  1. features(w) 호출 → vector<bool> 임시 객체 생성
  2. operator[](5) 호출 → vector<bool>::reference 반환
    • vector<bool>::reference임시 객체 내부 비트 포인터 + offset(5) 를 보유
  3. autovector<bool>::reference 형식 연역
  4. 문장 끝에서 임시 객체 파괴 → reference가 가진 포인터가 댕글링 포인터가 됨

bool로 명시하면 왜 안전했는가?

  • vector<bool>::referencebool 암묵적 변환이 임시 객체가 살아있는 동안 발생하기 때문

🧩 프록시 클래스

  • 표현식 템플릿 기법을 사용하는 C++ 라이브러리에서도 흔히 쓰인다.
Matrix sum = m1 + m2 + m3 + m4;
  • Matrix 객체에 대한 operator+Matrix를 반환하는 것은 비효율적이다.
  • 대신, Sum<Matrix, Matrix>와 같은 프록시 클래스 객체를 반환한다.
Matrix sum = Sum<Sum<Sum<Matrix, Matrix>, Matrix>, Matrix>;
  • 프록시 객체가 연쇄된 형태로 변환된다.
    • 사용자가 프록시 객체의 존재를 모르게 하는 것이 좋다.
    • 즉, 프록시 객체를 직접 쓰지 않도록 해야 한다.
  • 프록시 클래스는 Matrix로의 암묵적 변환을 지원하기에, 효율적으로 처리할 수 있다.
namespace std {
	template <class Allocator>
	class vector<bool, Allocator> {
	public:
		class reference { ... };
		reference operator[](size_type n);
	};
}
  • vector<bool>::operator[]의 명세이다.
  • operator[]T&이 아니라 reference(프록시 객체) 를 반환한다.
    • 이렇듯, 직접 보기 전까지 프록시 객체의 존재를 알지 못해야 한다.

✅ 해결 방법: static_cast

auto highPriority = static_cast<bool>(features(w)[5]);

// Matrix도 이제 auto를 사용할 수 있음
auto Matrix = static_cast<Matrix>(m1 + m2 + m3 + m4);
  • 형식 명시 초기치 관용구static_cast를 사용하면 된다.
  • features(w)[5]referencebool(캐스팅)이 된다.
    • auto도 이제 정상적으로 bool로 연역된다.

추가 사용법:

// double을 반환하는 함수
double calcEpsilon();

// 명시적 (double -> float가 의도적인 것인가?)
float ep = calcEpsilon();
// auto + static_cast (float 변환이 의도된 것임을 알 수 있음)
auto ep = static_cast<float>(calcEpsilon());
// 명시적 (double -> int가 의도된 것인가?)
int idx = d * c.size(); // d * c.size() = double형
if(idx == c.size()) --idx;

// auto + static_cast
auto idx = static_cast<int>(d * c.size());
if(idx == c.size()) --idx;
  • 변환이 의도된 것임을 알리는 데 사용할 수도 있다.

🧐 정리

  • “보이지 않는” 프록시 형식 때문에 auto가 초기화 표현식의 형식을 잘못 연역할 수 있다.
  • 형식 명시 초기치 관용구(static_cast)auto가 원하는 형식을 연역하도록 강제한다.

Cpp 카테고리 내 다른 글 보러가기

댓글남기기