C++ 컴파일러는 f.foo<P>()와 같은 구문을 파싱할 때 f.foo < P > ()와 같이 비교 연산자로 해석할 수 있는 모호성을 가집니다. 이는 F의 타입이 컴파일 시점에는 확정되지 않아 F::foo가 일반 데이터 멤버인지, 아니면 템플릿 멤버 함수인지 알 수 없기 때문에 발생합니다. std::function<int()>와 같이 std::function이 템플릿임을 컴파일러가 미리 아는 경우와는 대조적입니다.
의존적인 멤버 템플릿 호출 해결
이러한 모호성을 해결하기 위해 C++ 표준은 f.template foo<P>()와 같이 template 키워드를 사용하여 foo가 템플릿임을 명시하도록 요구합니다. 이 template 키워드는 컴파일러에게 foo 뒤에 오는 <P>가 비교 연산자가 아닌 템플릿 인자 목록임을 알려주는 역할을 합니다.
의존적인 중첩 타입 참조 해결
유사하게, 의존적인 중첩 타입(예: F::Nested)을 참조할 때도 파싱 모호성이 발생할 수 있습니다. F::Nested() 구문에서 Nested가 F의 정적 멤버 함수를 의미하는지, 아니면 F 내부에 선언된 중첩 클래스 Nested의 생성자를 의미하는지 컴파일 시점에는 구분할 수 없습니다. 이 경우 typename F::Nested()와 같이 typename 키워드를 사용하여 Nested가 타입임을 명시해야 합니다.
더 나아가, 중첩된 템플릿 클래스를 참조할 때는 typename F::template Nested<P>()와 같이 typename과 template 키워드를 모두 사용해야 합니다.
C++20 이후의 완화된 규칙
최근 C++ 표준에서는 이러한 어노테이션이 선택 사항이 되는 일부 진전이 있었습니다. 예를 들어, template<typename B> struct T : B::type {};와 같이 B::type이 반드시 타입이어야만 유효한 문맥에서는 typename 키워드가 불필요하며, C++20부터는 함수 반환 타입과 같은 특정 상황에서도 typename을 생략할 수 있게 되었습니다. 그러나 의존적인 함수 이름 호출과 같은 복잡한 경우에는 여전히 template 키워드가 필수적입니다.