본문 바로가기
C++/Effective Modern C++

typedef보다 별칭 선언

by COCO1337 2020. 7. 1.
typedef std::unique_ptr<std::unordered_map<std::string, std::string>> UPtrMapSS;	// C++98
using UPtrMapSS = std::unique_ptr<std::unordered_map<std::string, std::string>>;	// C++11

typedef와 별칭 선언이 하는 일은 동일하다.

먼저, 함수 포인터가 관여하는 형식을 다룰 때는 별칭 선언 쪽을 더 쉽게 하는사람들이 많다.

// FP는 int 하나와 const std::string& 하나를 받고 아무것도 돌려주지 않는 함수와 동의어이다.
typedef void (*FP)(int, const std::string&);	// typedef
using FP = void (*)(int, const std::string&);	// 별칭 선언

2, 3열은 동일한 의미이다. 하지만 사실 둘다 어려운건 마찬가지다.


별칭 선언이 강력한 이유는 템플릿에서 나온다. typedef는 템플릿화 할 수 없지만 별칭 선언은 템플릿화 할 수 있다. 템플릿화 된 별칭 선언을 별칭 템플릿(alias template)라고 부른다.

예를들어 MyAlloc라는 커스텀 allocator를 사용하는 linked list의 동의어를 정한다고 하자.

// 별칭선언
template<typename T>
using MyAllocList = std::list<T, MyAlloc<T>>;
// MyAllocList<T>와 std::list<T, MyAlloc<T>>는 동의어
MyAllocList<Widget> lw;

// typedef
template<typename T>
struct MyAllocList {
	typedef std::list<T, MyAlloc<T>> type;
};
// MyAllocList<T>::type은 std::list<T, MyAlloc<T>>는 동의어
MyAllocList<Widget>::type lw;

C++98에서는 템플릿화된 struct안에 내포된 typedef를 사용해야 표현 할 수 있던 것을 별칭 선언을 통해 좀 더 직접적으로 표현할 수 있게 된다.

만약 템플릿 매개변수로 지정된 형식의 객체들을 담는 linked list를 생성하려는 목적으로 템플릿 안에서 typedef를 사용하려 한다면, typedef 앞에 typename을 붙여야 한다.

template<typename T>
class Widget {
private:
	typename MyAllocList<T>::type list;	// Widget<T>에는 MyAllocList<T>형식의 자료 멤버가 있다
	...
};

이 예에서 MyAllocList<T>::type은 템플릿 형식 매개변수(T)에 의존적인 형식을 지칭한다. 즉 MyAllocList<T>::type은 dependent type이며 이름 앞에 반드시 typename을 붙여야 한다. 하지만 별칭선언을 사용하면

template<typename T>
using MyAllocList = std::list<T, MyAlloc<T>>;

template<typename T>
class Widget {
private:
	MyAllocList<T> list;
	...
};

typename과 ::type을 붙일 필요가 없다.

 

MyAllocList<T>::type처럼 MyAllocList<T>도 여전히 템플릿 매개변수 T에 의존하는 것 같지만 컴파일러는 Widget 템플릿을 처리하는 과정에서 MyAllocLiost<T>가 쓰인 부분에 도달했을 때, 컴파일러는 그 MyAllocList<T>가 형식의 이름임을 알고있다. MyAllocList가 형식 템플릿이므로 MMyAllocList<T>는 반드시 형식 이름이어야 하기 때문이다. 즉, MyAllocList<T>는 비의존적 형식이며, typename 지정자를 붙여서는 안된다.

반면, Widget 템플릿 안에서 MyAllocList<T>::type을 만난 컴파일러는 그것이 형식의 이름임을 확신하지 못한다. 어떤 특수화에서 MyAllocList<T>::type이 형식 이외의 어떤 것을 지칭할 가능성이 남아있기 때문이다.

class Wine{...};

template<>
class MyAllocList<Wine> {	// T가 Wine인 경우에 대한 MyAllocList의 특수화
private:
    enum class WineType
    { White, Red, Rose };
    WineType type;		// type은 자료 멤버
}

이러한 경우 MyAllocList<T>::type이 형식을 지정하지 않는다. 따라서, Widget 템플릿 안에서 MyAllocList<T>::type이 형식을 지칭하는지는 전적으로 T가 무엇인지에 의존한다. 그렇기 때문에 반드시 typename을 붙여야 한다.


때때로 템플릿 형식 매개변수를 받아서 적절히 변경한 형식을 만들어 내는 것이 필요한 템플릿 메타 프로그래밍에서는 어떤 형식 T에 담긴 임의의 const 한정사나 기타 참조 한정사들을 제거해야 하는 경우가 있다.

C++11은 이런 변환을 형식 특질(type trait)의 형태로 수행할 수 있는 도구들을 제공한다. 이런 변환들은 적용할 때 각 용례의 끝에 ::type이라는 접미어를 붙인다. 이 얘기는 모두 템플릿화된 struct 안에 내포된 typedef로 구현되었다는 사실이다. 그렇기 때문에 C++14에서는 std::변환_t 형태의 별칭 템플릿을 만들어 포함시켰다.

std::remove_const<T>::type	// C++11 : const T -> T
std::remove_const_t<T>		// C++14
std::remove_reference<T>::type	// C++11 : T&/T&& -> T
std::remove_reference_t<T>	// C++14

template <class T>
using remove_const_t = typename remove_const<T>::type;
template <class T>
using remove_reference_t = typename remove_const<T>::type;

conclusion

- typedef는 템플릿화를 지원하지 않지만, 별칭 선언은 지원한다.

- 별칭 템플릿에서는 "::type" 접미어를 붙일 필요가 없다. 템플릿 안에서 typedef를 지칭할 때에는 "typename" 접두사를 붙여야 하는 경우가 많다.

- C++14는 C++11의 모든 형식 특질 변환에 대한 별칭 템플릿들을 제공한다.


Reference

Effective Modern C++ 항목 9: typedef보다 별칭 선언을 선호하라

반응형

댓글