ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Effective Modern C++] using과 typedef
    C++/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의 사용이 권장된다고 볼 수 있습니다.

     

    감사합니다.

     

    댓글

Designed by Tistory.