일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- effective modern c++
- 비교 함수 객체
- operator new
- 영화
- 티스토리챌린지
- resource management class
- 오블완
- implicit conversion
- lua
- reference
- c++
- 다형성
- 상속
- Effective c++
- more effective c++
- 스마트 포인터
- 게임
- virtual function
- 반복자
- exception
- 예외
- 메타테이블
- Smart Pointer
- 언리얼
- UE4
- 참조자
- 암시적 변환
- effective stl
- 함수 객체
- 영화 리뷰
Archives
- Today
- Total
스토리텔링 개발자
[Effective Modern C++] 6. auto의 타입 추론 실패 본문
728x90
항목 6. auto가 원치 않은 타입으로 추론될 때에는 명시적 타입의 초기치를 사용하라
auto 타입 추론 실패 상황
std::vector<bool> features(const Widget& w);
Widget w;
...
// 기존 처리
bool highPriority = features(w)[5];
processWidget(w, highPriority);
// auto를 사용한다면..
// 컴파일은 여전히 성공한다.
auto highPriority = features(w)[5];
processWidget(w, highPriority); // 미정의 행동!
- auto 버전에서는 features(w)[5]의 타입이 bool이 아니기 때문이다.
- std::vector<bool>의 operator[]가 리턴하는 것은 std::vector<bool>::reference 타입의 객체이다.
- std::vector<bool>::reference?
- std::vector<bool>은 bool들을 실제 bool이 아니라 1비트의 합축된 형태로 가진다.
- 하지만 C++에서는 비트에 대한 참조가 금지되어 있다.
- 즉, bool&처럼 작동하는 객체를 돌려주는 프록시 클래스 객체이다.
- 이 객체는 bool로의 암시적 변환을 지원한다.
- 미정의 행동을 하는 이유
- highPriority는 features 함수를 호출한다.
- features 함수는 std::vector<bool> 임시 객체를 돌려준다.
- 그 임시 벡터 객체로부터 std::vector<bool>::reference 객체를 리턴 받는다.
- 하지만, 이 모든 게 끝난 후에는 std::vector<bool> 임시 객체는 파괴된다.
- 결국, 리턴 받은 std::vector<bool>::reference 객체 내부에서 가리키는 std::vector<bool>에 대한 포인터는 댕글링 포인터가 된다.
프록시 클래스
- 다른 타입의 행동을 흉내내고 보강하는 클래스 (MEC++ 항목 30 참조)
- 클라이언트가 명확히 알 수 있게 설계된 것들
- std::shared_ptr, std::unique_ptr
- 다소 은밀하게 작동하도록 설계된 것들
- std::vector<bool>::reference, 표현식 템플릿(expression template) 기법을 사용하는 라이브러리(Matrix 등)
- 대체로 수명이 한 문장 이상일 거라고 가정하지 않고 설계되어 있다.
- 대체로 은밀하게 작동하는 프록시 클래스는 auto와 잘 맞지 않는다.
- 그러므로 다음과 같은 형태의 코드는 피하도록 해야 한다.
auto someVar = "보이지 않는" 프록시 클래스 타입의 표현식;
프록시 객체의 존재를 확인하는 방법
- 라이브러리 문서를 확인한다.
- 헤더 파일에서 프록시 객체의 흔적을 확인한다.
- 대체로 프록시 객체는 클라이언트가 호출하도록 만들어진 어떤 함수가 돌려준다.
-
// std::vector<bool>::operator[]의 명세 namespace std { template <class Alocator> class vector<bool, Allocator> { public: .... class reference { ... }; // 프록시 클래스 reference operator[](size_type n); // 프록시 객체를 리턴함을 확인 가능하다. ... }; }
auto 추론 실패 해결법
- auto를 버린다?
- auto 자체는 문제가 아니므로 진정한 해법이 아니다.
- auto가 다른 타입을 추론하도록 강제한다.
- 타입 명시 초기치 관용구(explicitly typed initializer idiom)을 사용하여 해결.
- 변수를 auto로 선언하되,
- 초기화 표현식의 타입을 auto가 추론하길 원하는 타입으로 캐스팅한다.
auto highPriority = static_cast<bool>(features(w)[5]);
auto sum = static_cast<Matrix>(m1 + m2 + m3 + m4);
- 물론 프록시 클래스 타입에만 적용할 수 있는 건 아니다.
- 초기화에 쓰이는 표현식이 산출하는 타입과는 다른 타입으로 변수를 생성하고자 하는 의도를 명확히 할 때도 쓰인다.
// 1.
double calcEpsilon();
float ep = calcEpsilon(); // float로 암시적 변환이 발생.
// 하지만, 함수가 돌려준 값의 정밀도를 일부러 줄이고자 한다는 의도가 확실히 보이지 않는다.
auto ep = static_cast<float>(calcEpsilon()); // 타입 명시 초기치 관용구
// 2.
// 0.0 ~ 1.0 사이의 double 값으로 리스트의 요소 위치값을 산출하려 한다.
int idx = d * c.size(); // int로 암시적 변환이 발생.
if (idx == c.size()) --idx; // d == 1.0일때도 유효하도록 처리
// 하지만, 일부러 int로 변환했다는 의도가 명확하지 않다.
auto idx = static_cast<int>(d * c.size()); // 타입 명시 초기치 관용구
if (idx == c.size()) --idx; // d == 1.0일때도 유효하도록 처리
728x90
'개발 > Effective Modern C++' 카테고리의 다른 글
[Effective Modern C++] 5. 타입 명시보다 auto (0) | 2025.02.11 |
---|---|
[Effective Modern C++] 4. 추론 타입 파악하기 (0) | 2025.02.10 |
[Effective Modern C++] 3. decltype (0) | 2025.02.07 |
[Effective Modern C++] 2. auto 타입 추론 규칙 (0) | 2025.02.06 |
[Effective Modern C++] 1. 템플릿 타입 추론 규칙 (0) | 2025.02.05 |
Comments