KoreanFoodie's Study

Effective Modern C++ | 항목 4 : 연역된 형식을 파악하는 방법을 알아두라 본문

Tutorials/C++ : Advanced

Effective Modern C++ | 항목 4 : 연역된 형식을 파악하는 방법을 알아두라

GoldGiver 2022. 10. 26. 09:49

C++ 프로그래머의 필독서이자 바이블인, 스콧 마이어스의 Modern Effective C++ 를 읽고 기억할 내용을 요약하고 있습니다. 꼭 읽어보시길 추천드립니다!

항목 4 : 연역된 형식을 파악하는 방법을 알아두라

핵심 :

1. 컴파일러가 연역하는 형식을 IDE 편집기나 컴파일러 오류 메시지, Boost TypeIndex 라이브러리를 이용해서 파악할 수 있는 경우가 많다.
2. 일부 도구의 결과는 유용하지도 않고 부정확할 수 있으므로, C++ 의 형식 연역 규칙들을 제대로 이해하는 것은 여전히 필요한 일이다.


형식 연역 정보를 얻는 방법은 세 가지 시점으로 분류할 수 있다.

1. IDE 편집기

2. 컴파일러의 진단 메시지

decltype 을 사용하면 다음과 같은 경우, 타입을 컴파일 타임에 알아낼 수 있다.

template<typename T>
class TD;

int main()
{
	auto x  = 1.0f;
	TD<decltype(x)> xType;

	auto y = (x);
	TD<decltype(y)> yType;
}

다음과 같은 에러 메시지를 출력한다!
aggregate 'TD<float> xType' has incomplete type and cannot be defined (yType 도 동일함)

3. 실행시점 출력

typeid 와 std::typeinfo::name 등을 사용해서 printf 를 하면, 런타임에 해당 변수들의 타입을 알아낼 수 있다.

auto x  = 1.0f;
const int* y = new int(10);

std::cout << typeid(x).name() << std::endl; // f
std::cout << typeid(y).name() << std::endl; // PKi

물론 결과값은 컴파일러마다 달라서, 해석하기 어려운 경우도 있다. 그리고 typeid 가 제대로 동작하지 않는 경우도 있는데, 다음 예시를 보자.

class Widget {};

template<typename T>
void f(const T& param)
{
	using namespace std;
	// T 를 표시 : T = PK6Widget
	cout << "T = " << typeid(T).name() << endl;
	// param 을 표시 : param = PK6Widget
	cout << "param = " << typeid(param).name() << endl;
}

// 팩터리 함수
std::vector<Widget> createVec() { return std::vector<Widget>(10); }


int main()
{
	const auto vw = createVec();

	if (!vw.empty())
	{
		f(&vw[0]);
	}
}

PK 는 "const 를 가리키는 포인터" 를 뜻한다. 숫자 6은 그냥 클래스 이름(Widget) 의 글자 수이다. 즉, T 와 param 이 둘다 const Widget* 형식임을 출력하고 있다. 즉, const Widget* 라는 뜻이다.
그런데, T 와 param 의 형식이 같은 것은 이상하다. T 는 const Widget* 가 맞지만, param 은 const Widget* const & 형식을 가져야 하기 때문이다. 사실 이는 표준을 준수한 것으로, std::type_info::name 은 주어진 형식을 마치 템플릿 함수에 값 전달매개변수로서 전달된 것처럼 취급해야 한다. 항목 1에서 설명했듯, 값 전달의 경우 참조와 const 성이 제거되어 param 의 형식이 const Widget* 이 된 것이다.

대신 Boost.TypeIndex 을 이용하면 정확한 타입을 얻을 수 있다.

#include <boost/type_index.hpp>

class Widget {};

template<typename T>
void f(const T& param)
{
	using namespace std;
	using boost::typeindex::type_id_with_cvr;

	// T 를 표시 : T = Widget const*
	cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
	// param 을 표시 : param = Widget const* const&
	cout << "param = " << type_id_with_cvr<decltype(param)>().pretty_name() << endl;
}

boost::typeindex::type_id_with_cvr 은 전달된 형식 인수의 const 나 volatile, 참조 한정사들을 그대로 보존한다!

Comments