공부/그 외

C7510 : 종속적 형식 이름은 'typename' 접두사와 함께 사용해야 합니다.

sudo 2021. 8. 12. 01:15

Hash Table을 구현하다가 제목과 같은 오류를 만났다.

 

아래와 같은 코드를 작성하다가 만났다. 아래 코드에서 "에러 발생 지점"이라고 표시한 부분을 잘 보자

template <typename Key, typename Value>
class CHashNode
{
	template <typename Key, typename Value, int HASHSIZE>
	friend class CHashTable;

	template <typename Key, typename Value>
	friend class CHashTableIterator;

private:
	CHashNode()
	{
	}

	~CHashNode()
	{
    		// 에러 발생 지점
    		// 아래 코드 2줄 맨 앞에 typename을 붙여줘야 한다.
		CList<CHashNode<Key, Value>*>::iterator iter = m_Chain.begin(); 
        	// 에러 발생 지점
		CList<CHashNode<Key, Value>*>::iterator iterEnd = m_Chain.end();

		for (; iter != iterEnd; ++iter)
		{
			delete *iter;
		}
	}

// 다른 구현한 멤버 함수, 멤버 변수들...

};

CList 클래스는 이전에 구현했던 클래스를 가져왔다.

template <typename T>
class CListIterator
{
	template <typename T>
	friend class CList;

public:
	CListIterator()	:
		m_Node(nullptr)
	{
	}

	~CListIterator()
	{
	}

private:
	CListNode<T>* m_Node;

public:
	// iterator끼리 서로 가지고 있는 노드가 같을 경우 같다고 판단한다.
	bool operator == (const CListIterator<T>& iter)	const
	{
		return m_Node == iter.m_Node;
	}

	bool operator != (const CListIterator<T>& iter)	const
	{
		return m_Node != iter.m_Node;
	}

	bool operator == (const CListNode<T>* Node)	const
	{
		return m_Node == Node;
	}

	bool operator != (const CListNode<T>* Node)	const
	{
		return m_Node != Node;
	}

	void operator ++ ()
	{
		m_Node = m_Node->m_Next;
	}

	void operator ++ (int)
	{
		m_Node = m_Node->m_Next;
	}

	void operator -- ()
	{
		m_Node = m_Node->m_Prev;
	}

	void operator -- (int)
	{
		m_Node = m_Node->m_Prev;
	}

	T& operator * ()	const
	{
		return m_Node->m_Data;
	}
};
template <typename T>
class CList
{
public:
	CList()
	{
		m_Size = 0;

		// Begin과 End노드를 생성하고 두 노드를 서로 연결한다.
		m_Begin = new NODE;
		m_End = new NODE;

		m_Begin->m_Next = m_End;
		m_End->m_Prev = m_Begin;
	}

	~CList()
	{
		// Begin노드는 이전노드가 nullptr이다.
		// End노드는 다음노드가 nullptr이다.
		PNODE	DeleteNode = m_Begin;

		while (DeleteNode)
		{
			PNODE	Next = DeleteNode->m_Next;

			delete	DeleteNode;

			DeleteNode = Next;
		}
	}

private:
	typedef CListNode<T>	NODE;
	typedef CListNode<T>*	PNODE;

public:
	typedef CListIterator<T>	iterator;

private:
	// Begin과 End는 데이터를 저장하기 위한 노드는 아니다.
	// 명시적으로 시작과 끝을 의미하는 노드로 사용하기 위해 할당해두고 사용한다.
	// 실제 데이터를 저장하는 노드는 Begin과 End노드 사이에 위치하게 될것이다.
	PNODE	m_Begin;
	PNODE	m_End;
	int		m_Size;

	iterator begin()	const
	{
		iterator	iter;
		iter.m_Node = m_Begin->m_Next;
		return iter;
	}

	iterator end()	const
	{
		iterator	iter;
		iter.m_Node = m_End;
		return iter;
	}

	void operator = (const CList<T>& list)
	{
		// 기존 정보를 제거한다.
		clear();

		m_Size = list.m_Size;

		PNODE	Node = list.m_Begin->m_Next;

		while (Node != list.m_End)
		{
			push_back(Node->m_Data);
			Node = Node->m_Next;
		}
	}
    
    // 나머지 멤버 함수들은 생략...

};

 

CList<CHashNode<Key, Value>*>::iterator iter = m_Chain.begin();

이 코드에서 맨 앞에 typename을 붙여줘야 하는 이유는 컴파일러 입장에서 CList<CHashNode<Key, Value>*>::iterator 이것이 타입인지 아닌지 모르기 때문이다. 

 

예를 들어서 아래 예시를 보자

template <class T>
void foo() {
   T::iterator * iter;
   ...
}

class ContainsAType {
   class iterator { ... }:
   ...
};

int main()
{
    foo<ContainsAType>();
}

이렇게 foo를 호출하면서 T인자로 ContainsAType을 넘겨줄 때 foo() 안에서는 "ContainsAType안에 iterator라는 타입이 있군, 그래서 iter은 그 iterator 타입의 포인터구나" 문제는 이걸 ContainsAType을 넘겨줄 때가 되서야 안다는 것이지 컴파일러가 foo함수 안에서 T::iterator *iter라는 코드를 만났을 때는 이걸 모른다는 것이다.

 

컴파일러 입장에서는 이런 경우라고 생각할 수도 있다.

template <class T>
void foo() {
   T::iterator * iter;
   ...
}

class ContainsAValue {
   static int iterator;
};

int main()
{
    foo<ContainsAType>();
}

이러면 컴파일러는 ContainsAValue안에 iterator변수와 iter의 곱하기로 인지할 수도 있다. 말하고자 하는 요지는 컴파일러는 저 T::iterator *iter라는 코드를 만났을 때는 저게 타입인지 아닌지 구별할 수 없다는 것이다.

 

따라서 템플릿인자에 의존적인 타입을 사용할때는 항상 typename을 붙여주자.

 

Reference

http://pages.cs.wisc.edu/~driscoll/typename.html#dependent

 

A Description of the C++ typename keyword

The purpose of this document is to describe the reasoning behind the inclusion of the typename keyword in standard C++, and explain where, when, and how it can and can't be used. Note: This page is correct (AFAIK) for C++98/03. The rules have been loosened

pages.cs.wisc.edu

https://onecoke.tistory.com/entry/%EC%9D%98%EB%AC%B8%EC%A0%90-%EC%98%A4%EB%A5%98-C2760-C7519-%EA%B5%AC%EB%AC%B8-%EC%98%A4%EB%A5%98-%EC%8B%9D%EB%B3%84%EC%9E%90%EC%9D%80%EB%8A%94-%EC%98%88%EA%B8%B0%EC%B9%98-%EC%95%8A%EC%9D%80-%ED%86%A0%ED%81%B0%EC%9E%85%EB%8B%88%EB%8B%A4-%ED%95%84%EC%9A%94%ED%95%9C-%ED%86%A0%ED%81%B0%EC%9D%80-%EC%9E%85%EB%8B%88%EB%8B%A4

 

<문제점> 오류) C2760, C7519 // 구문 오류: '식별자'은(는) 예기치 않은 토큰입니다. 필요한 토큰은 ';'

1 2 3 4 5 6 7 8 9 10 11 12 13 template void Safe_Delete_VecList(T& p) {     T::iterator iter;     T::iterator iterEnd = p.end();     for (iter = p.begin(); iter != iterEnd; ..

onecoke.tistory.com