-
[Effective Modern C++] using과 typedefC++/Effective Modern C++ 2022. 4. 5. 17:30
typedef는 동의어를 만들어 주는 예약어 입니다.
예를 들어 "std::unique_ptr<std::unordered_map<std::string, std::string>>"같은 형식이 있고, 이 형식을 여러 차례 선언해야 할 경우 코드 가독성도 떨어질 수 있고, 타이핑의 양이 매우 많아질 수 있습니다.
typedef std::unique_ptr<std::unordered_map<std::string, std::string>> uptr_map_str_str;
이 경우, 위와 같은 식으로 형식을 축약할 수 있는 이름을 붙여주는 것이 typedef의 주요한 목적입니다.
이번 글에서는 typedef와 더불어 C++11에서 나온 새로운 별칭 선언 예약어인 using에 대하여 살펴 볼 것입니다.
1. using
더보기using은 C++11에서 새로 추가된 별칭 선언 (Alias declaration)예약어 입니다.
이 예약어는 typedef와 기능이 동일합니다.
예를 들어, 서론의 typedef예제의 경우
using uptr_map_str_str = std::unique_ptr<std::unordered_map<std::string, std::string>>;
위와 같이 using으로 표현이 가능합니다.
typedef와 비교하여, using을 이용한 표현은 특정 경우에서 가독성이 좋습니다.
예제를 살펴보도록 하겠습니다.
typedef void (*FP)(int, const std::string&); using FP = void (*)(int, const std::string&);
위 예제는 파라미터로 int와 const std::string&을 받고, 반환형이 void인 함수 포인터를 FP라고 정의 한 것입니다.
두 경우 모두 코드를 읽는 데 아주 난해하지는 않지만, typedef용법을 "typedef A B"의 형태로 접하는 경우가 잦기 때문에, 함수 포인터와 관련된 위 상황에서 다소 혼란이 올 수 있습니다.
2. Alias template
더보기템플릿화 된 별칭 선언을 별칭 템플릿 (Alias template)이라고 합니다.
템플릿화에 있어, typedef는 struct내부의 typedef를 사용하는 등의 편법을 이용해야 하지만 using은 템플릿화 할 수 있습니다.
예제를 살펴보도록 하겠습니다.
//Template with using template<typename T> using MyAllocList_U = std::list<T, MyAlloc<T>>; MyAllocList_U<MyClass> a; //Template with typedef template<typename T> struct MyAllocList_T { typedef std::list<T, MyAlloc<T>> type; }; MyAllocList_T<MyClass>::type a;
MyAlloc이라는 커스텀 Allocator를 사용하는 std::list에 대한 별칭 선언을 위와 같이 할 수 있습니다.
typedef는 직접적인 템플릿화가 불가능하기 때문에 구조체를 통해 간접적으로 이용한 것을 볼 수 있습니다.
그에 반해, using은 템플릿화가 직접적으로 가능하여 상대적으로 간결한 것을 볼 수 있습니다.
조금 더 나아가서, 위 list를 직접적으로 사용하는 것이 아닌, 또 다른 템플릿 매개변수를 이용하는 것을 가정해보도록 하겠습니다.
구체적으로 아래의 예제와 같습니다.
template<typename T> class MyClass { private: typename MyAllocList_T<T>::type list; };
위 예제의 경우, MyAllocList_T<T>::type은 템플릿 매개변수 T에 의존적인 형식입니다.
C++에는 이러한 의존적인 형식의 이름 앞에 typename을 붙여야 하는 규칙이 존재하여, 해당 선언에는 typename 예약어를 사용해야 합니다.
그렇지 않을 경우, 아래와 같이 컴파일이 거부됩니다.
C4346 : 'type': 종속 이름이 형식이 아닙니다. C2061 : 구문 오류: 식별자 'type' C2238 : ';' 앞에 예기치 않은 토큰이 있습니다.
using은 조금 더 자유롭습니다.
template<typename T> class MyClass { private: MyAllocList_U<T> list; };
얼핏 보면 typedef때와 같이 T에 의존적인 형식으로 보이지만, 그렇지 않습니다.
MyAllocList_U가 형식 템플릿이기 때문에 MyAllocList_U<T>가 형식의 이름이어야 하는 것을 컴파일러가 알고 있기 때문입니다. 따라서 MyAllocList_U<T>는 비 의존적인 형식이고, typename을 요구하지 않습니다.
typedef를 사용한 용례의 의존성에 대해 조금 더 살펴보도록 하겠습니다.
다음 예제는 typedef를 사용한 MyAllocList_T의 한 특수화 입니다.
template<> struct MyAllocList_T<int> { private: enum class Flag {A, B, C}; Flag type; };
위와 같이 특수화를 할 경우, MyAllocList_T<int>::type은 형식을 지정하지 않고, 특정한 자료 멤버를 지정합니다.
따라서 MyAllocList_T<T>::type이 어떤 것을 지정하는지는 T에 의존적인 것 입니다. 컴파일러는 위와 같이 특수화를 통해 type이 형식이 아닐 가능성을 배제할 수 없기 때문에 typename을 요구하게 되는 것 입니다.
3. <type_traits>
더보기C++의 <type_traits> 헤더 안에 있는 여러 함수들은 const, &등의 참조 한정사들을 제거하거나, 추가하는 역할을 합니다. C++11에 추가된 이 기능들은 std::[변환]<T>::type 의 형식을 취합니다.
예제를 살펴보도록 하겠습니다.
std::add_const<T>::type; std::remove_const<T>::type; std::remove_reference<T>::type; std::add_lvalue_reference<T>::type;
함수명이 직관적이고, 함수의 기능을 소개하는 것이 이번 문단의 목적이 아니기 때문에 생략하도록 하겠습니다.
중요한것은, 이 함수들이 typedef로 구현되어 있다는 것 입니다.
이러한 표준들은 C++14에 using을 이용한 별칭 템플릿으로 변환되었습니다.
바뀐 기능들은 아래와 같습니다.
std::add_const_t<T>; std::remove_const_t<T>; std::remove_reference_t<T>; std::add_lvalue_reference_t<T>;
기존의 함수들에 _t가 붙고, using을 사용했기 때문에 ::type이 사라졌습니다.
_t를 붙인 이유는, 기존 C++11에 있는 함수들이 그대로 남아있기 때문입니다.
using과 typedef에 대하여 알아보았습니다.
주된 내용이 템플릿과 관련된 내용이었던 것은, 아마 그 부분이 가장 크게 다르기 때문이라고 생각합니다.
using이 typedef보다 간결한 것과, C++11에서 typedef를 사용한 함수들을 using으로 변경한 점 등, 전체적으로 using의 사용이 권장된다고 볼 수 있습니다.
감사합니다.
'C++ > Effective Modern C++' 카테고리의 다른 글
[Effective Modern C++] private와 delete (0) 2022.04.22 [Effective Modern C++] enum과 enum 클래스 (0) 2022.04.18 [Effective Modern C++] 0과 NULL과 nullptr (0) 2022.03.30 [Effective Modern C++] 괄호 ()와 중괄호 {} 그리고 Uniform initialization (0) 2022.02.16 [Effective Modern C++] auto와 std::vector, 그리고 Proxy pattern (0) 2022.02.14