Skip to content
C++
Language
since C++98
Advanced

Dependent Names

Names in a template whose meaning depends on a template parameter, subject to two-phase lookup and requiring typename or template disambiguation.

Dependent Namesince C++98

A dependent name is any name inside a template whose meaning depends on a template parameter, causing the compiler to defer its lookup and binding until the template is instantiated with concrete arguments.

Overview

Templates introduce a split in when names are resolved. The standard mandates two-phase name lookup: non-dependent names are bound the moment the compiler parses the template definition; dependent names are bound only at instantiation when concrete template arguments are known.

A name is dependent if it:

  • Is qualified by a type that depends on a template parameter (T::member)
  • Appears in a context where argument types depend on template parameters (enabling ADL at phase two)
  • Is accessed through a pointer or reference to a dependent type (ptr->member where ptr has type T*)

Non-dependent names are blind to declarations made after the template definition, even if instantiation happens after those declarations:

cpp
void log(double) { /* ... */ }

template<typename T>
struct Engine {
    void run() {
        log(1);  // non-dependent: bound to log(double) at definition time
    }
};

void log(int) { /* ... */ }  // declared after template definition

// Engine<int>{}.run() still calls log(double), not log(int)

This is well-defined and intentional—templates must not silently change behaviour as new declarations accumulate in surrounding translation units.

Syntax

The typename Disambiguator

When a dependent qualified name refers to a type, the compiler cannot determine—during phase one—whether T::A denotes a type, a static value, or a template. The standard defaults to treating it as a value. The typename keyword instructs the compiler to treat the name as a type:

cpp
template<typename Container>
void fill_sorted(Container& c) {
    typename Container::iterator begin = c.begin(); // typename required
    typename Container::value_type sentinel{};      // typename required
    // ...
}

Without typename, the compiler parses Container::iterator begin as a multiplication expression (Container::iterator times begin), producing a confusing hard error.

C++20 relaxation: In grammatical positions where only a type is valid, typename is no longer required. These include alias declarations, return type positions, template type arguments, and a handful of other contexts:

cpp
// C++20: typename optional in alias and return-type positions
template<typename T>
using ValueType = T::value_type;          // no typename needed (C++20)

template<typename T>
T::size_type size_of(const T& c) {        // no typename needed in return type (C++20)
    return c.size();
}

Before C++20, both require typename. Code targeting C++17 or earlier must always prefix dependent type names with typename, even in these positions.

The template Disambiguator

A parallel ambiguity affects dependent template names. When the compiler sees obj.name<X>, it cannot tell whether < opens a template argument list or is the less-than operator. The template keyword, placed immediately before the member name after ., ->, or ::, resolves the ambiguity:

cpp
template<typename Alloc>
void allocate_block(Alloc& a) {
    // Without template keyword, a.allocate<int>(n) is parsed as
    // (a.allocate < int) > (n), which is ill-formed.
    auto* p = a.template allocate<int>(16);
}

template<typename T>
struct Registry {
    template<typename K>
    T* lookup(K key);
};

template<typename T, typename K>
T* find(Registry<T>& reg, K key) {
    return reg.template lookup<T>(key); // reg is dependent; template keyword required
}

Dependent Base Class Members

Unqualified lookup does not examine dependent base classes during phase one. Inherited members of a base that depends on a template parameter are invisible to plain unqualified name lookup inside a derived class template:

cpp
template<typename T>
struct Base {
    void init() {}
    int value = 0;
};

template<typename T>
struct Derived : Base<T> {
    void setup() {
        // init();         // ERROR: Base<T> is dependent, not examined in phase 1
        // int x = value;  // ERROR: same reason

        this->init();       // OK: this->init is a dependent expression, resolved in phase 2
        Base<T>::init();    // OK: qualified dependent name, also resolved in phase 2
        int x = this->value;
    }
};

this-> is the idiomatic fix. It converts an unqualified non-dependent lookup into a dependent member access, deferring resolution until instantiation when the concrete base class is known.

Examples

Iterating with a Dependent Iterator Type

cpp
template<typename Container>
void print_all(const Container& c) {
    for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) {
        std::cout << *it << '\n';
    }
    // C++11 range-for or auto avoids the typename spelling entirely:
    for (const auto& elem : c) {
        std::cout << elem << '\n';
    }
}

Calling a Templated Member on a Dependent Type

cpp
template<typename Stream>
void write_header(Stream& s) {
    s.template put<uint32_t>(0xCAFEBABE);
    s.template put<uint16_t>(1);
    s.template put<uint16_t>(0);
}

CRTP Requiring this-> for Base Members

cpp
template<typename Derived>
struct Comparable {
    bool operator!=(const Derived& other) const {
        // Uses Derived's operator== through static_cast; no dependent lookup issue here.
        return !static_cast<const Derived*>(this)->operator==(other);
    }
};

template<typename T>
struct Point : Comparable<Point<T>> {
    T x, y;
    bool operator==(const Point& other) const {
        return x == other.x && y == other.y;
    }
};

Alias Templates Eliminating typename Noise (C++11)

Before C++11, rebinding allocators required typename at every use site. Alias templates introduced in C++11 make the rebound type non-dependent from the consumer's perspective:

cpp
// Old pattern: nested typedef requires typename at every use site
template<typename T, typename Alloc>
struct OldRebind {
    typedef typename Alloc::template rebind<T>::other type;
};
// Caller must write: typename OldRebind<int, MyAlloc>::type

// C++11 alias template: non-dependent at use site, no typename required
template<typename T, typename Alloc>
using ReboundAlloc = typename Alloc::template rebind<T>::other;
// Caller writes: ReboundAlloc<int, MyAlloc>

Common Pitfalls

Missing typename on nested types: Compiler errors like "expected primary-expression before 'it'" or "expression is not a constant" inside a template often indicate a missing typename. The parser is treating the qualified name as a value expression.

Missing template on member templates: Compilers typically emit "expected primary-expression before <" when < is parsed as less-than instead of a template argument opener. Adding template before the member name resolves it.

Assuming base class members are visible: Unlike non-template inheritance, a class template does not implicitly search its dependent base class for unqualified names. Every use of an inherited member in a derived template must go through this-> or a qualified name—this catches many engineers by surprise when migrating non-template code to templates.

Phase-one violations with no diagnostic required: If a non-dependent name's meaning differs between the definition context and the instantiation context, the program is ill-formed with no diagnostic required (IFNDR). Many compilers silently accept this in permissive modes, creating latent portability bugs that surface only on stricter toolchains or under -std=c++17 with -pedantic.

Best Practices

  • Prefer alias templates over nested typedefs when authoring library APIs—consumers avoid typename noise at every use site.
  • Access all inherited members through this-> in derived class templates unconditionally. It documents intent, satisfies strict compilers, and is trivially verified by a grep.
  • In C++20 codebases, rely on the relaxed typename rules in return-type and alias positions, but keep typename in expression contexts where it aids readability.
  • When a template function calls a templated member on a dependent type, document the requirement explicitly—e.g., // requires T::serialize<U>(archive)—so callers understand the template keyword at the call site.
  • Prefer auto for local variables over spelling out dependent type names like typename Container::iterator; reserve explicit typename for function signatures and type aliases where auto is not available.

Deep Dive: typename Disambiguation

The Type-vs-Value Ambiguity

The root cause is that the C++ grammar is ambiguous without knowing whether a qualified name denotes a type. Consider:

cpp
template<typename T>
void process() {
    T::value_type * ptr;  // ambiguous without typename
}

The compiler cannot tell in phase one whether this is:

  1. A pointer declaration: (T::value_type) * ptr — if value_type is a type
  2. A multiplication expression: (T::value_type) * (ptr) — if value_type is a static variable

The standard's default is to assume non-type (i.e., the multiplication interpretation). To declare a pointer, you must disambiguate:

cpp
template<typename T>
void process() {
    typename T::value_type * ptr;  // unambiguous: pointer declaration
}

Concrete Error Without typename

cpp
template<typename Container>
size_t count_positive(const Container& c) {
    Container::const_iterator it = c.begin();  // ERROR (C++17 and earlier)
    //  ^~~ compiler sees this as: (Container::const_iterator) * (it = c.begin())
    //  i.e., a multiplication, which is ill-formed as a statement
    return 0;
}

Typical compiler error (GCC/Clang): error: need 'typename' before 'Container::const_iterator' because 'Container' is a dependent scope

The fix — prefix with typename:

cpp
template<typename Container>
size_t count_positive(const Container& c) {
    typename Container::const_iterator it = c.begin();  // OK
    size_t n = 0;
    for (; it != c.end(); ++it)
        if (*it > 0) ++n;
    return n;
}

Where typename Is Required (Before C++20)

typename is required whenever a dependent qualified name is used as a type in an expression context — i.e., anywhere the compiler cannot grammatically infer that a type is expected:

cpp
template<typename T>
struct Traits {
    // Data member declaration — typename required
    typename T::value_type default_val;

    // Local variable — typename required
    void foo() {
        typename T::size_type n = 0;
    }

    // In a template argument that is a type — typename required
    std::vector<typename T::value_type> storage;
};

Two pre-C++20 exceptions where typename was already not required:

  • Base class specifier: template<typename T> struct D : T::Base { } — base class position unambiguously expects a type
  • Constructor member initializer id: Foo() : T::Base() { } — also unambiguously a type

Where typename Is Forbidden

You cannot write typename on the name of a dependent base class in the base-class list:

cpp
template<typename T>
struct Derived : typename T::Base { };  // ERROR: typename forbidden here
//               ^^^^^^^^ ill-formed

template<typename T>
struct Derived : T::Base { };           // OK: base-class position implies type

Deep Dive: template Disambiguation

The Less-Than Ambiguity

When a name is accessed through a dependent expression (via ., ->, or ::) and is followed by <, the compiler faces another ambiguity: is < the start of a template argument list, or the less-than operator?

cpp
template<typename T>
void example(T obj) {
    obj.convert<int>(0);
    // Parsed as: (obj.convert < int) > (0)
    // i.e., two comparisons, not a template call
}

The template keyword signals that the < is a template argument list opener:

cpp
template<typename T>
void example(T obj) {
    obj.template convert<int>(0);  // OK: template member call
}

Syntax Rules for template

The template keyword can appear after ., ->, or :: when the expression to the left is type-dependent or value-dependent:

cpp
template<typename T>
void all_forms(T& obj, T* ptr) {
    obj.template method<int>();         // dot access
    ptr->template method<int>();        // arrow access
    T::template static_method<int>();   // scope resolution
}

It can also appear in the middle of a qualified name chain before a class template used as a scope:

cpp
// typename and template used together for a nested template type
template<typename T>
void nested(T& t) {
    typename T::template rebind<int>::other storage;
    //                 ^^^^^^^^ template disambiguates rebind<int>
    //         ^^^^^^^^ typename disambiguates ::other as a type
}

When Is template Required?

The rule: template is required when all of the following hold:

  1. The name appears after ., ->, or ::
  2. The left-hand side of the accessor is a dependent expression (its type depends on a template parameter)
  3. The name being called is a function template (takes template arguments)

If the left-hand side is not dependent, the compiler already knows at phase one whether the name is a template and template is not needed:

cpp
std::vector<int> v;
v.emplace_back<int>(42);   // OK without 'template' — v is not dependent
                            // (though emplace_back rarely takes explicit args)

CRTP and Dependent Base Class Lookup

Why CRTP Forces this->

The Curiously Recurring Template Pattern (CRTP) inherits from a base parameterized on the derived type itself. This creates a dependent base class — and the standard explicitly excludes dependent bases from unqualified name lookup during phase one.

cpp
template<typename Derived>
struct Printable {
    void print() const {
        // Access derived data through CRTP downcast
        const Derived& self = static_cast<const Derived&>(*this);
        std::cout << self.to_string() << '\n';
    }
};

template<typename T>
struct Value : Printable<Value<T>> {
    T data;
    std::string to_string() const {
        return std::to_string(data);
    }

    void display() {
        print();        // ERROR: 'print' is not found — Printable<Value<T>> is dependent
        this->print();  // OK: this-> makes the lookup dependent (phase 2)
    }
};

The compiler rejects print() during phase one because Printable<Value<T>> depends on T — it could be specialized for some T to not have print at all. By writing this->print(), the call becomes a dependent expression and is deferred to phase two, where the concrete instantiation is known and the base class is fully available.

Why the Standard Excludes Dependent Bases

The exclusion exists to prevent silent ambiguity. If unqualified lookup searched dependent bases, then a specialization could silently inject or remove names, changing the meaning of code in the derived template without any error:

cpp
template<typename T> struct MaybeHasX { };
template<> struct MaybeHasX<int> { int x = 42; };

int x = 0;  // global x

template<typename T>
struct Reader : MaybeHasX<T> {
    int read() { return x; }  // which x? global always — no silent override
};

// If lookup searched dependent bases:
// Reader<double>{}.read()  → returns global 0
// Reader<int>{}.read()     → might silently return member 42 — surprising!

The standard mandates that x is non-dependent here and is always bound to the global x at definition time. To read the member, you must explicitly qualify it: this->x or MaybeHasX<T>::x.

Three Ways to Access Dependent Base Members

All three techniques make the name dependent, deferring lookup to phase two:

cpp
template<typename T>
struct Base {
    using size_type = std::size_t;
    size_type count = 0;
    void reset() { count = 0; }
};

template<typename T>
struct Derived : Base<T> {
    // Option 1: this-> (idiomatic for member function calls and data)
    void clear() { this->reset(); }

    // Option 2: qualified name (explicit, slightly more verbose)
    void clear2() { Base<T>::reset(); }

    // Option 3: using declaration in class body (makes name non-dependent at use site)
    using Base<T>::reset;
    using typename Base<T>::size_type;  // typename required for type names
    void clear3() { reset(); }          // now found via 'using'
};

Option 3 (a using declaration) is best when a base member is accessed frequently — it avoids repeating this-> or Base<T>:: at every call site. However, it must be placed in the class body and repeated for each name.


C++20: Fewer typename Requirements

P0634 "Down with typename!"

C++20 adopted proposal P0634, which removed the typename requirement in all grammatical positions where only a type is valid. The compiler can statically determine from syntax alone that a type is expected in these contexts, making typename redundant.

Contexts where typename is now optional in C++20:

ContextC++17C++20
using alias member declarationstypename T::type requiredT::type sufficient
Non-static data member declarationstypename T::type requiredT::type sufficient
Function parameter typestypename T::type requiredT::type sufficient
Trailing return typetypename T::type requiredT::type sufficient
Default template type argumenttypename T::type requiredT::type sufficient
static_cast / cast type-idtypename T::type requiredT::type sufficient

Before and After C++20

cpp
// C++17: typename noise throughout
template<typename T>
struct Iterator17 {
    using value_type      = typename T::value_type;
    using difference_type = typename T::difference_type;
    using pointer         = typename T::pointer;
    using reference       = typename T::reference;

    typename T::pointer   ptr_;

    explicit Iterator17(typename T::pointer p) : ptr_(p) { }

    typename T::reference operator*() const { return *ptr_; }
};
cpp
// C++20: typename omitted where the grammar implies a type
template<typename T>
struct Iterator20 {
    using value_type      = T::value_type;
    using difference_type = T::difference_type;
    using pointer         = T::pointer;
    using reference       = T::reference;

    T::pointer   ptr_;

    explicit Iterator20(T::pointer p) : ptr_(p) { }

    T::reference operator*() const { return *ptr_; }
};

Where typename Is Still Required in C++20

C++20 did not remove typename from expression contexts, where the type-vs-value ambiguity remains genuine:

cpp
template<typename T>
void still_needs_typename(T& obj) {
    // Expression context: ambiguous between type and value
    typename T::value_type local{};   // still required in C++20

    // But in declaration/type contexts it's gone:
    T::value_type local2{};           // also OK in C++20 — declaration context
}

The practical rule for C++20: typename is still required inside function bodies when the dependent type name appears in an expression (e.g., constructing a temporary, casting). In declaration contexts (data members, using declarations, parameter types, return types), it is optional.

Compiler Support for P0634

P0634 support arrived at different times across compilers:

  • GCC: 9.0 (released 2019) — full support
  • MSVC: Visual Studio 2019 16.10 — full support
  • Clang: 16 (released 2023) — full support

Code written for C++20 and targeting all three major compilers should avoid relying on P0634 unless building with GCC 9+ / MSVC 16.10+ / Clang 16+.

See Also

  • reference/language/adl — argument-dependent lookup runs at phase two for dependent names but not phase one; understanding both together prevents subtle overload surprises
  • reference/language/class-template — template parameter declarations, explicit instantiation, and the instantiation model that drives two-phase lookup
  • reference/language/decltype — an alternative to spelling out dependent type names; decltype expressions may themselves be dependent
  • reference/language/auto — type deduction sidesteps most typename boilerplate for local variables inside template bodies