item 5 에서 auto를 사용해서 얻게 되는 장점을 살펴 봤다.
하지만 auto를 사용하게 되면서 문제가 생기는 경우가 있기 때문에 주의해야할 부분이 있다.
the explicitly typed initializer idiom
auto type deduction이 정확히 어떤 것으로 되는지 알 수 없는 경우, 문제가 생긴다.
물론 auto 그 자체에 문제가 있는 것은 아니다.
std::vector<bool> features(const Widget& w);
Widget w;
…
bool highPriority = features(w)[5]; // is w high priority?
-> auto highPriority = features(w)[5];
…
processWidget(w, highPriority); // process w in accord with its priority
highPriority 값을 bool 로 받아서 함수 processWidget의 파라미터로 전달하는 내용에서
highPriority를 auto 로 선언하는 방식으로 바꿨을 때, 우리가 기대하는 동작은 bool 로 type deduction이 일어나 동일한 동작이 수행되는 것이지만, 실제로는 예기치 않은 동작이 발생하게 된다.
std::vector<bool> 의 operator[] 가 bool의 reference를 반환하는 것이 아니라, std::vector<bool> reference 타입의 객체를 리턴하게 되기 때문이다.
std::vector<T> 의 operator[] 는 T&를 반환해야 하는데 C++에서는 비트로 표현되는 참조값은 금지되어 있기 때문에 객체를 반환해서 bool& 처럼 행동할 수 있도록 만든 것이다.
문제는 auto duduction에서 std::vector<bool>::reference가 어떻게 구현되어 있는지에 따라 값을 가지는 것이 달라진다.
구현 방법 중 하나는 pointer 와 offset을 가진 객체로 된 것이다.
features 함수는 temporary std::vector<bool> 객체를 반환하고(temp 객체)
temp 객체는 operator[] 로 std::vector<bool>::reference를 호출한다.
이 때 반환되는 것은 temp 객체에 대한 pointer 와 offset을 가진 객체 참조값인데 highpriority 가 temp 객체에 대한 pointer를 가지게 되어 instruction이 끝나게 되면 결국 dangling pointer를 가지는 문제가 발생한다.
이러한 문제의 해결 방법은 explicitly typed initializer idiom 이다.
auto highPriority = static_cast<bool>(features(w)[5]);
auto sum = static_cast<Matrix>(m1 + m2 + m3 + m4);
이 방식은 proxy class type에만 제한되어서 사용되는 개념이 아니다.
초기화 표현식에 의해 만들어진 타입과 다른 타입의 변수를 생성할 때에 강조하기 위해 사용될 수 있다.
double calcEpsilon(); // return tolerance value
//float ep = calcEpsilon(); // impliclitly convert double → float
auto ep = static_cast<float>(calcEpsilon());
int index = d * c.size();
auto index = static_cast<int>(d * c.size());
proxy class
proxy class는 여러가지 목적으로 이용된다.
std::vector<bool>::reference 는 proxy class의 목적 중에서 어떤 다른 type의 행동을 대신할 목적으로 존재하는 클래스이다.
a proxy class: a class that exists for the purpose of emulating and augmenting the behavior of some other type.
리소스 관리를 하기 위해 원래 포인터를 std::shared_ptr, std::unique_ptr와 같은 스마트 포인터를 이용해 관리하는 것도 proxy class의 한 종류이다.
Invisible proxy class
스마트 포인터의 경우 우리눈에 잘 보이는 proxy class 이지만, std::vector<bool>::reference 의 경우 invisible proxy class의 한 종류이다.
invisible proxy class 의 예로 std::bitset::reference 도 존재하고 expression templates 에 해당하는 라이브러리에서 몇몇 클래스들도 invisible proxy class 들이 있다.
Matrix 객체는 numeric 계산을 효율적으로 하기 위한 객체인데 Matrix는 operator+ 연산 결과를 Sum<Matrix, Matrix> 형태의 proxy 로 return 한다.
보통 invisible proxy class 들이 auto 와 사용될 때 제대로 동작하지 않는 경우가 많다.
따라서, 아래와 같은 형태의 코드는 피해야 한다.
auto someVar = expression of "invisible" proxy class type;
보이지 않는데 어떻게 invisible proxy class가 사용되는지 알 수 있을까?
첫번째는 document에 익숙해져서 proxy class가 사용되는지를 파악할 수 있다.
두번째는 header file들을 살펴봐서 놓칠 수 있는 부분들을 매꿀 수 있다.
namespace std { // from C++ Standards
template <class Allocator>
class vector<bool, Allocator> {
public:
…
class reference { … };
reference operator[](size_type n);
…
};
}
위의 예시처럼 C++ standards 에서 vector 사용에 reference가 사용되고 있는 것을 바로 확인할 수 있다.
'Effective Modern C++' 카테고리의 다른 글
EMC++ Item 8: Prefer nullptr to 0 and NULL. (0) | 2022.08.28 |
---|---|
EMC++ item 7 : Distinguish between () and {} when creating objects. (0) | 2022.08.28 |
EMC++ item 5 : 명시적 형식 선언보다는 auto를 선호하라 (0) | 2022.08.15 |
EMC++ item 4 : 연역된 형식을 파악하는 방법을 알아두라 (0) | 2022.08.15 |
EMC++ item 3 : decltype의 작동 방식을 숙지하라 (0) | 2022.08.15 |
댓글