41. 두 단계 이름 조회와 의존 이름, typename/template/this-> 명시자
난이도 상내 리뷰 · C++
해설 · C++
해설 — 두 단계 이름 조회와 의존 이름, typename/template/this-> 명시자
난이도: 상
요약
템플릿 이름 조회는 두 단계다. 1단계(정의 시점): 템플릿 인자에 의존하지 않는
비의존 이름을 조회. 2단계(인스턴스화 시점): 의존 이름을 조회(이때 ADL
포함). 컴파일러는 파싱 시점에 이름이 타입인지/템플릿인지 몰라 기본으로 "타입이
아님·템플릿이 아님"으로 가정하므로, 프로그래머가 typename/template/this->
로 힌트를 줘야 한다.
핵심 개념
- 의존 이름(dependent name): 의미가 템플릿 인자에 따라 달라지는 이름
(
C::value_type,Base<T>::useBase,p->get). 이들의 조회는 2단계로 미뤄진다. - 비의존 이름: 인자와 무관 → 1단계에서 즉시 조회·바인딩(정의 시점의 스코프로).
- 파서의 기본 가정: 의존 이름은 (a) 타입이 아니다, (b) 템플릿이 아니다 로 간주. 그래서 명시자가 필요.
단계별 내부 동작
- this->useBase() —
Base<T>는 의존 기반 클래스라, 1단계에서 파생 클래스가useBase를 자기 스코프/전역에서 찾을 때 의존 기반의 멤버는 보지 않는다 (기반이 어떤 특수화가 될지 아직 모름). 그냥useBase()라 쓰면 1단계에서 "선언되지 않은 식별자" 에러.this->useBase()또는Base<T>::useBase()로 쓰면 의존 이름이 되어 2단계(인스턴스화)에서 실제 기반을 대상으로 조회된다. - typename C::value_type —
C::value_type은 의존 이름이라 파서가 기본으로 "값/멤버(비타입)"로 가정한다.typename을 빼면typename C::value_type v = ...를 "C::value_type 이라는 값에 무언가를 곱하거나 선언 문법 오류"로 해석해 에러.typename이 "이건 타입이다"를 알려 선언으로 파싱된다. - p->template get<0>() —
get은 의존 이름이고 뒤에<가 온다. 파서는 기본으로get을 "템플릿이 아님"으로 가정하므로<를 작다 연산자로 파싱해p->get < 0 > ()처럼 깨진다.template키워드가 "get 은 멤버 템플릿"임을 알려<0>을 템플릿 인자로 파싱한다. - 조회 시점과 ADL — 비의존 이름은 1단계(정의 시점 스코프)에서 확정된다. 의존 이름의 함수 호출은 2단계에서 조회되며, 이때 일반 조회는 정의 시점 스코프 기준이지만 ADL(인자 의존 조회)은 인스턴스화 시점의 인자 타입 네임스페이스까지 본다. 그래서 정의 후에 추가된 네임스페이스 함수라도 ADL 로 걸릴 수 있다(오버로드 선택이 미묘해지는 지점).
흔한 오해·함정
- "파생 클래스에서 기반 멤버는 그냥 보인다" → 의존 기반이면
this->/한정 필요. MSVC 는 과거 이를 관대하게 받아줘(비표준) 이식 시 GCC/Clang 에서 깨진다. .template/->template/::template이 필요한 자리를 빠뜨려<파싱이 깨짐.- C++20 이라도 규칙은 동일(두 단계 조회는 유지).
면접 포인트
- 왜 두 단계로 나누는가(정의 시점엔 어떤 특수화로 인스턴스화될지 모름).
typename/template/this->각각이 파서에게 알려주는 것.- 의존 이름의 ADL 이 인스턴스화 시점 스코프를 보는 점과 그 함정.