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.

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