본문 바로가기

공부기록/C, C++

[C++] typedef 와 typename 그리고 중첩의존타입이름

typedef와 typename 키워드에 대해 알아보고, 의존타입이 뭔지,
그리고 nested dependent type name 을 식별하기 위해 꼭 typename을 써야하는 경우를 알아봤다.

typedef

타입의 새로운 별칭을 정의하는 키워드

class MyTypeClass
{
public:
    typedef int Type1;
};

int main()
{
    MyTypeClass a;
    MyTypeClass::Type1 b;
    return 0;
}

typedef 사용시 장점

1. 읽기 편하고, 입력하기 편하다.

2. 호환성이 좋아진다.

  • 플랫폼에 따라서 이름을 달리 써야 하는 경우나 호환이 안되는 이름에 대해서 새로운 플랫폼으로 옮기는 작업이 간편해진다.
#if defined USING_COMPILER_A
    typedef __int32 Int32;
    typedef __int64 Int64;
#elif deined USING_COMPILER_B
    typedef int Int32;
    typedef long long Int64;
#endif

3. 유연성이 생긴다.

  • typedef 된 이름을 한 곳에서 바꾸는 것이 코드 전체를 찾아서 이름이 쓰인 곳마다 바꾸는 것보다 훨씬 간단하다.
  • 만약 아래 코드에서 vector의 사용이 적절치 않았다고 판단되면, 나중에 이를 list나 deque로 손쉽게 변경할 수 있다.
typedef vector<Customer> Customers;
typedef Customers::iterator CustIter;
//...
void f( Customers& custs )
{
    CustIter i = custs.begin();
    //...
}

템플릿 파라미터의 별칭

아래와 같이 템플릿 파라미터의 별칭을 지을 수도 있다.

  • 이처럼 템플릿 내부의 이름 중에 템플릿 매개변수에 종속된 것을 의존 이름(dependent name)이라고 한다. (의존 타입이라고도 함)
  • 여기선 TempType이 의존이름.
  • 의존 이름이 클래스 안에 중첩되어 있으면 중첩 의존 타입 이름(nested dependent type name) 이라고 한다. 여기서도 클래스 안에 TempType 이라는 타입이 있으니까 TempType은 중첩 의존 타입 이름이다.
#include <iostream>

template<typename T>
class MyTempClass
{
public:
    typedef T TempType;
};

int main()
{
    MyTempClass<int> a;
    MyTempClass<int>::TempType b;
    MyTempClass<double> c;
    MyTempClass<double>::TempType d;
    return 0;
}

 

실질적인 예시 => STL의 iterator

  • 컨테이너를 받아서 처음부터 끝까지 루프 돌면서 모든 원소를 출력하는 코드 입니다. 컨테이너가 벡터, 리스트, 데크 인지도 알 수 없지만 컨테이너 원소가 int, double, char 형인지 도 알 수 없습니다. 하지만 각 컨테이너 안에 상수반복자를 typedef 키워드로 const_iterator 라고 정의 했기에 어떤 원소를 담은 어떤 컨테이너가 들어와도 함수가 동작 할 수 있습니다.
template <class T>
inline void PRINT_ELEMENTS (const T& coll, const char* optcstr="")
{
    typename T::const_iterator pos;
      // typename 키워드는 다음에 언급합니다
 
    std::cout << optcstr;
    for (pos=coll.begin(); pos!=coll.end(); ++pos) {
        std::cout << *pos << ' ';
    }
    std::cout << std::endl;
}
 

 

 

 


typename

  • 템플릿 매개변수를 선언하는 경우에는 typename과 class 키워드는 완벽하게 동일하게 동작한다.
template <class T> class Widget;
template <typename T> class Widget;
  • 하지만 이 두 키워드가 항상 동등한 것은 아니다.

중첩 의존 타입 이름을 식별하는 용도에서는 반드시 typename을 사용해야 한다.

이유

  • 만약 위의 STL iterator 예제에서 typename 키워드 없이 T::const_iterator pos; 만으로 선언을 했다면, 컴파일러는 const_iterator가 T 클래스 내부의 멤버변수일거라고 생각해버릴 수도 있다. 아니, 컴파일러는 이런 경우 기본적으로 타입이 아니라고 가정한다.
  • 따라서 c++ 컴파일러에게 어떤 키워드는 typedef로 재정의 된 type이라는 것을 알려주기 위해 typename 키워드를 사용해야한다.
  • 아래 코드는 iterator가 vector<T>의 의존타입이기 때문에 iterator가 type이라는걸 알려주기 위해 typename 키워드를 사용해야한다. 
template <typename T>
void print_vector(std::vector<T>& vec) {
  // 전체 벡터를 출력하기
  for (typename std::vector<T>::iterator itr = vec.begin(); itr != vec.end();
       ++itr) {
    std::cout << *itr << std::endl;
  }
}
int main() {
  std::vector<int> vec;
  vec.push_back(10);
  vec.push_back(20);
  vec.push_back(30);
  vec.push_back(40);

  std::cout << "벡터 상태" << std::endl;
  print_vector(vec);
  std::cout << "----------------------------" << std::endl;

	return 0;
}
위 코드에서 typename을 왜 써주나 궁금해서 이 포스팅을 작성하게 되었다..!
궁금증해결!

 

 


참고

* https://wikidocs.net/420

* modoocode.com/223

* Effective C++ 3판

   +) 이 책에 중첩의존이름이지만 typename을 생략해도 되는 case가 설명되어 있는데 아직 이해못함