C++ Template Disambiguator for Dependent Types
In C++, parsing template code before instantiation poses a unique
syntactic challenge known as dependent name resolution. When a member
function template belongs to a dependent type—a type that relies on a
template parameter—the compiler cannot inherently determine whether a
trailing < symbol denotes the start of a template
argument list or a mathematical less-than operator. To resolve this
syntax ambiguity, C++ requires the template keyword prefix,
acting as a disambiguator that explicitly instructs the parser to treat
the following identifier as a template name.
The Cause of Syntax Ambiguity: Two-Phase Lookup
C++ utilizes a two-phase name lookup mechanism when processing templates:
- Phase 1 (Parsing): The template definition is parsed before any template arguments are supplied. The compiler validates basic syntax without knowing the concrete types represented by template parameters.
- Phase 2 (Instantiation): The template arguments are provided, concrete types are substituted, and the compiler generates machine code.
During Phase 1, when the compiler encounters an expression like
obj.method<Arg>(), where obj has a
dependent type T, it must parse the statement without
knowing what T actually is. By default, the language
grammar dictates that dependent member names are assumed to refer to
non-template members (such as fields or regular methods).
Because of this rule, the compiler reads:
obj.method < Arg > ();as:
((obj.method < Arg) > ());The parser interprets method as an ordinary member
variable, < as the less-than comparison operator, and
> as the greater-than operator. This leads to a
compilation error because () cannot follow a relational
operator.
How the
template Disambiguator Works
The template disambiguator resolves this issue by
informing the Phase 1 parser that the member name following the dot
(.), arrow (->), or scope resolution
(::) operator is a member template. This alters the grammar
rules applied by the compiler, forcing it to parse the subsequent
< as the opening bracket of a template argument list
(<...>) rather than a relational operator.
Syntax Rules
The disambiguator is inserted directly after the member access operator:
- Dot operator:
instance.template method<T>() - Arrow operator:
pointer->template method<T>() - Scope resolution operator:
Type::template method<T>()
Code Example
Consider a generic wrapper that calls a member template:
template <typename T>
void execute(T& obj) {
// Error: Compiler assumes 'process' is not a template.
// '<' is parsed as a less-than operator.
// obj.process<int>();
// Correct: 'template' disambiguates the name.
obj.template process<int>();
}If obj.process<int>() is written without
template, the compiler generates an error stating that
< is unexpected or that the dependent member is not
recognized as a template. With
obj.template process<int>(), the parser processes
<int> as the explicit template argument list,
allowing Phase 1 parsing to succeed.
Conditions Requiring the Disambiguator
The template prefix is not always necessary for member
template calls. It is strictly required only when all of the following
conditions are met:
- The member access depends on a template parameter: If the calling object’s type is fully known (non-dependent), the compiler already has the class definition and knows whether the member is a template.
- The member itself is a template: Non-template
dependent member calls require no special prefix, while dependent types
themselves require the
typenamedisambiguator instead. - Template arguments are explicitly specified using
<>: If template argument deduction can infer the types without angle brackets (e.g.,obj.process(10)), no<symbol exists to trigger the parsing ambiguity, and thetemplatekeyword is unnecessary.