2016년 3월 9일 수요일

Effective Modern C++, Item4: 추론 타입을 확인하는 방법

타입 추론을 확인하기 위해 사용되는 툴은 개발 프로세스에 따라 달라진다. 여기서는 다음과 같은 세 가지 상태에 대해서 알아본다: 코드 에디트, 컴파일 단계, 런타임.

IDE 에디터

const int theAnswer = 10;
auto x = theAnswer;
auto y = &theAnswer;
IDE 에디터(Visual Studio 등)는 변수에 마우스를 올리면 x는 int로, y는 const int*로 추론되었다는 것을 보여줄 것이다.

이렇게 작동하게 하기 위해서 IDE는 내부적으로 C++ 컴파일러를 동작시킨다. 만약 이 컴파일러가 타입을 추론하기에 부족한 상태라면 추론된 타입을 보여줄 수 없다. 따라서, int 같은 단순한 형태라면 문제가 없지만 더 호환성 있는 타입이 사용된다면 IDE에 표시되는 정보는 유용하지 않을 수 있다.

컴파일러 진단


컴파일러를 사용하여 타입을 추론하는 효과적인 방법은 추론된 타입을 컴파일 에러가 나도록 사용하는 것이다. 이렇게 하면 에러 메세지를 통해 추론된 변수의 타입을 확인할 수 있다.

예를 들면, 먼저 아래와 같은 형태의 정의되지 않은 템플릿을 만들 수 있다.
template<typename T>
class TD;
그리고 이러한 템플릿을 인스턴스화 하는 것은 에러 메시지를 발생시킬 것이다. 위의 예제에서의 x와 y의 타입을 알고 싶다면 아래와 같이 사용할 수 있다.
TD<decltype(x)> xType;
TD<decltype(y)> yType;

런타임 결과

실행 화면에서 표시될 수 있는 텍스트의 형태로 타입을 출력하기 위해서 아래와 같은 방법을 사용할 수 있다.
std::cout << typeid(x).name() << '\n';
std:;cout << typeid(y).name() << '\n';
컴파일러마다 표시되는 문자가 다를 수 는 있지만 마이크로소프트의 컴파일러는 정확한 결과를 보여준다. 하지만 그렇다고 문제가 해결된 것은 아니다. 조금 더 복잡한 경우를 살펴보자.
template<typename T>
void f(const T& param);
std::vector<Widget> createVec();
const auto vw = createVec();
if(!vm.empty()) {
f(&vm[0]);
...
}
위 코드에 typeid를 확인하기 위해서 f 함수를 아래와 같이 정의한다.
template
void f(const T& param)
{
  using std::cout;
  cout << "T = " << typeid(T).name() << '\n';
  cout << "param = " << typeid(param).name() << '\n';
  ...
컴파일 결과는 마이크로소프트 컴파일러의 경우 둘 다 class Widget const * 로 표시된다.
하지만 소스에서 param 의 타입은 const T&로, T와는 다르다. 이처럼 std::type_info::name 은 항상 신뢰할 수 는 없다.

그 이유는 std::type_info::name의 특성 상 템플릿 함수를 by-value 파라미터로 받는다는데 있다. 만약 타입이 레퍼런스이면, 그 레퍼런스의 특징이 삭제(무시)되고, 삭제된 레퍼런스가 const라면 그 또한 삭제된다. 따라서 const Widget * const & 가 const Widget * 이 된 것이다.

이러한 현상은 IDE에서도 동일하게 일어난다. 해결방법 중 하나는 Boost TypeIndex 라이브러리를 사용하는 것이다. Boost를 사용하면 다음과 같이 표현할 수 있다.
#include 

template
void f(const T& param)
{
  using std::cout;
  using boost::typeindex::type_id_with_cvr;

  //show T
  cout << "T = " << type_id_with_cvr().pretty_name() << '\n';

  //show param's type
  cout << "param = " << type_id_with_cvr().pretty_name() << '\n';
  ...
}
이것이 작동하는 방법은 boost::typeindex::type_id_with_cvr 이 우리가 알고자 하는 변수를 인자로 받아서 const와 volatile, 혹은 & 성질을 지우지 않고 이용하는 것이다. .pretty_name()은 인자의 속성을 std::string으로 반환해준다.

이 같은 방법을 사용하면 T의 타입은 Widget const* 로, param의 타입은 Widget const* const&로 정확하게 알 수 있다.

댓글 없음:

댓글 쓰기