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을 왜 써주나 궁금해서 이 포스팅을 작성하게 되었다..!
궁금증해결!
참고
* Effective C++ 3판
+) 이 책에 중첩의존이름이지만 typename을 생략해도 되는 case가 설명되어 있는데 아직 이해못함
'공부기록 > C, C++' 카테고리의 다른 글
[C++] map 출력하기 (0) | 2021.04.20 |
---|---|
[BOJ] 기타 입출력 문제 C++ (0) | 2020.10.24 |
[BOJ] 입출력 A+B 문제 C++ (0) | 2020.10.18 |
[C++] 포인터 대신 참조자(reference)를 써야하는 경우는 ? (0) | 2020.07.11 |
[C] 이중포인터의 선언, 할당, 참조 이해하기 (0) | 2020.06.12 |