std::vector<bool> features(const Widget& w) { ... }
...
auto highPriority = features(w)[5]; // std::vector<bool>::operator[](5)
/* 둘은 같은 코드일까? */
bool highPriority = feature(w)[5];

정답부터 얘기하면 둘의 타입은 다르다.

vector<bool>은 유일하게 템플릿 특수화에 의해 만들어지는 컨테이너이다. 또한 컨테이너의 요소를 바이트가 아닌 비트로 관리하고 있다.

그런데 C++에서 비트에 대한 참조는 금지되어있다는 사실을 안다면 vector<bool>::operator[]는 bool& 타입을 반환하지 못한다는 사실 역시 알 수 있다.

대신 마치 bool&처럼 작동하는 객체를 돌려주는 우회법을 사용한다.

 

vector<bool>::operator[]는 bool&이 아닌 vector<bool>::reference 타입을 반환해준다. 그리고 해당 타입은 실제 bool 타입에 대한 암시적 변환이 가능하게 해준다.

 

auto highPriority1 = features(w)[5]; // highPriority1 = std::vector<bool>::reference
bool highPriority2 = feature(w)[5]; // std::vector<bool>::reference의 bool로 암시적 변환

auto highPriority의 타입은 std::vector<bool>::reference가 되고 bool highPriority의 타입은 std::vector<bool>::reference의 암시적 변환 허용에 의해 정상적인 값이 대입된다. 물론 반환받은 값은 실제 vector의 요소가 아닌 임시로 생성된 bool 객체이다.

 

std::vector<bool>::reference는 스마트 포인터와 같이 프록시 패턴이 적용된 프록시 클래스의 일종이다.

operator[]가 마치 비트에 대한 참조를 돌려주는 듯한 환상을 제공해주는 것이다. 그런데 스마트 포인터와는 조금 다르게 캡슐화가 더 잘 되어있을 뿐이다.

 

 

Matrix sum = m1 + m2 + m3 + m4;

프록시 클래스는 표현식 템플릿 기법을 사용하는 라이브러리에서도 흔히 사용된다.

Matrix::operator+의 결과로 Matrix를 반환하는게 아니라 Sum<Matrix, Matrix>같은 프록시 클래스의 객체를 돌려주도록 하면 계산이 조금 더 효율적이다.

그런데 이걸 auto로 선언한다면 Sum<Sum<Sum<Matrix, Matrix>, Matrix>, Matrix> 같은 타입이 되기 때문에 사용자에게 보여줄 필요가 없다.

그래서 대체로 위와 같은 캡슐화가 잘된(보이지 않는) 프록시 클래스는 auto와 그다지 잘 맞지 않는다.

 

그러면 대체 어떻게하면 보이지 않는 프록시 클래스를 사용하고 있는지 파악할 수 있을까?

답은 의외로 간단하다. 타입 추론을 우리가 의도하는 타입으로 강제하는 것이다.

 

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

auto sum = static_cast<Matrix>(m1 + m2 + m3 + m4);

auto ep = static_cast<float>(calcEpsilon()); // 본래 반환값은 double

일부러 타입을 변환한다는 의도도 명확히 드러낼 수 있다.

 

◾ "보이지 않는" 프록시 타입 때문에 auto가 초기화 표현식의 타입을 "잘못" 추론 할 수 있다.

◾ 타입 명시 초기치 관용구는 auto가 원하는 타입을 추론하도록 강제한다.

+ Recent posts