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

Injected Class Name

Inside a class body, the class name is implicitly injected as a member, providing unqualified access to the current type or instantiation.

Injected Class Namesince C++98

Within the body of a class or class template, the class name is implicitly declared as a public member that refers to the class itself (or, for templates, to the current instantiation).

Overview

Every class definition silently introduces its own name into its own scope. This injected class name allows the class to refer to itself by unqualified name without explicit qualification, and it is what makes constructor declarations syntactically valid β€” the constructor Foo() is named using the injected class name Foo, not the global name ::Foo.

For non-template classes the effect is straightforward: Foo inside class Foo is equivalent to ::Foo. The real complexity emerges with class templates, where the injected name behaves differently depending on whether it appears in a type context or a template context.

Non-Template Classes

In a regular class, the injected class name is a synonym for the fully qualified type:

cpp
struct Node {
    int value;
    Node* next;   // Node here is the injected class name, same as ::Node
    Node* prev;

    Node(int v) : value(v), next(nullptr), prev(nullptr) {}
    // The constructor is 'Node' via the injected name β€” not '::Node()'

    Node clone() const { return *this; }  // return type uses the injected name
};

Inheritance makes the base class's injected name visible as an inherited member:

cpp
struct Base {
    void reset();
};

struct Derived : Base {
    Base* get_base() { return this; }  // 'Base' is inherited from Base's scope
};

This is why Base is accessible unqualified inside Derived β€” the injected class name of Base is a public member that gets inherited.

Class Templates

Inside a class template, the injected class name is an injected-class-name with implicit template arguments. When used as a type, it denotes the current instantiation; when used as a template-name (followed by <), it denotes the template itself.

cpp
template<typename T, typename Alloc = std::allocator<T>>
class Vec {
public:
    // 'Vec' as a type = Vec<T, Alloc> (current instantiation)
    Vec& operator=(const Vec&);          // Vec<T, Alloc>& = const Vec<T, Alloc>&
    Vec* clone() const;                  // returns Vec<T, Alloc>*

    // 'Vec' followed by '<' = the template, not the current instantiation
    template<typename U>
    Vec<U, std::allocator<U>> reinterpret() const;   // Vec<U,...>, a different specialization
};

The asymmetry matters most when you need to talk about a different specialization from within the class body. Using the bare name Vec always means "same template arguments as the current instantiation."

The Current Instantiation

The injected class name names what the standard calls the current instantiation. References to members through it are not dependent β€” the compiler resolves them without waiting for the template to be instantiated:

cpp
template<typename T>
class Ring {
    Ring* head;          // not dependent β€” Ring means Ring<T>
    Ring<T>* tail;       // identical meaning, explicit form

    void splice(Ring& other);   // Ring here = Ring<T>

    static Ring make() { return Ring{}; }  // Ring{} constructs Ring<T>
};

Compare this with accessing the name from outside, where you always need the template arguments:

cpp
Ring<int>* r = new Ring<int>();   // must be explicit outside the class body

CRTP and the Injected Name

The Curiously Recurring Template Pattern uses the template parameter, not the injected class name, to access the derived type. The injected name of the base only refers to the base:

cpp
template<typename Derived>
class Comparable {
public:
    bool operator<(const Comparable& rhs) const {
        // 'Comparable' here = Comparable<Derived> β€” the base, not Derived
        const Derived& lhs = static_cast<const Derived&>(*this);
        const Derived& rhs_d = static_cast<const Derived&>(rhs);
        return lhs.less_than(rhs_d);
    }
};

class Timestamp : public Comparable<Timestamp> {
public:
    bool less_than(const Timestamp& other) const {
        return epoch_ms < other.epoch_ms;
    }
private:
    long long epoch_ms;
};

Inside Comparable<Derived>, Comparable is the injected class name and means Comparable<Derived>. To refer to Derived, you use the template parameter directly.

Constructors and the Injected Name

Constructor declarations are a direct consequence of the injected class name rule. The constructor is declared using the class's name as it appears inside the class body:

cpp
class Connection {
public:
    Connection();                         // uses injected name
    Connection(const Connection&);        // copy constructor
    Connection& operator=(Connection&&);  // move assignment

    // ~Connection() uses the injected name prefixed with ~
    ~Connection();
};

Outside the class, the constructor is accessed as Connection::Connection, using the injected name as a qualified member β€” it can never be called as ::Connection().

Injected Name Lookup vs. Qualified Lookup

The injected class name participates in ordinary unqualified lookup from inside the class body. This can occasionally hide an outer declaration:

cpp
struct Buffer {};   // global

template<typename T>
struct Buffer {     // ERROR: redefinition β€” but illustrates name collision
};

// More subtle: injected name inside local scope
void process() {
    struct Context {
        struct Buffer { int size; };
        Buffer buf;    // injected name resolves to the local Buffer
        // ::Buffer is the global one
    };
}

When you need the outer name from inside, qualify explicitly.

Best Practices

Use the bare injected class name for return types and parameters within the class body β€” it is less noise than writing out template arguments explicitly, and it automatically tracks changes when you add or rename template parameters:

cpp
// Prefer:
template<typename T>
class Matrix {
    Matrix transpose() const;
    Matrix operator+(const Matrix& rhs) const;
};

// Over:
template<typename T>
class Matrix {
    Matrix<T> transpose() const;
    Matrix<T> operator+(const Matrix<T>& rhs) const;
};

When you genuinely need a different instantiation in a member function signature, use the explicit Matrix<U> form to make the intent visible.

Common Pitfalls

Assuming the injected name is accessible from outside the class. It is a member, so from external scope you must qualify: Matrix<float>::Matrix names the constructor; bare Matrix outside a template definition is a template-name, not a type.

Confusing the dual nature in templates. In a template-id context (followed by <), the injected name is the template; elsewhere it is the current instantiation:

cpp
template<typename T>
struct Tag {
    using same  = Tag;       // Tag<T>  β€” current instantiation
    using retyped = Tag<int>; // Tag<int> β€” different specialization; Tag here is the template
};

Expecting the injected name to propagate through using aliases or typedefs. Injected class names belong to the class scope, not to alias names:

cpp
template<typename T>
class Impl {};

using ImplInt = Impl<int>;
// Inside Impl<int>, the injected name is still 'Impl', not 'ImplInt'

Relying on it in dependent base classes. When a template inherits from a dependent base, the base's injected class name is not automatically visible. You must qualify it:

cpp
template<typename T>
struct Base {
    using value_type = T;
};

template<typename T>
struct Child : Base<T> {
    // value_type x;            // ERROR: dependent name, not found by unqualified lookup
    typename Base<T>::value_type x;  // correct
};

Name Lookup Mechanics: Injected vs. Outer Scope

The C++ standard specifies that a class name is inserted into the scope of the class itself immediately after the class name is seen β€” before the opening { is even reached. The injected class name is then treated as a public member for access-checking purposes, even if the class itself is private or protected in some enclosing context.

Unqualified lookup inside the class finds the injected name before searching enclosing scopes. This means an inner class can shadow an outer declaration with the same name:

cpp
struct Widget {};   // outer Widget

struct Outer {
    struct Widget {   // a nested class β€” also has injected name Widget inside itself
        Widget* sibling;  // injected name: refers to Outer::Widget, not ::Widget
    };

    Widget child;         // Outer::Widget (injected into Outer)
    ::Widget global;      // outer Widget via explicit qualification
};

The standard's exact words (C++20 [class.pre]/2): "The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For the purpose of access checking, the injected-class-name is treated as if it were a public member name."

Timing: Available from the Class Name Onward

The injection happens immediately after the class name appears in the declaration. This is why a class can declare pointers to itself even before its own definition is complete:

cpp
struct ListNode {
    int data;
    ListNode* next;   // valid: ListNode injected before '{' is processed
    ListNode* prev;

    static ListNode sentinel;   // also valid
};

No forward declaration of ListNode is needed β€” the injected name alone is sufficient.

Type Context vs. Template-Name Context

For class templates the injected name is context-sensitive. The standard distinguishes two situations:

  1. Type context β€” the injected name appears where a type is expected (variable declarations, return types, parameter types, base specifiers). Here it names the current instantiation: Vec means Vec<T, Alloc>.

  2. Template-name context β€” the injected name is immediately followed by <. Here it refers to the template itself, not the current instantiation. This allows you to form different specializations from within the class body.

cpp
template<typename T>
class Optional {
public:
    // Type context β€” Optional means Optional<T>
    Optional();
    Optional(const Optional&);             // copy ctor: takes const Optional<T>&
    Optional& operator=(Optional&&);       // Optional<T>& = Optional<T>&&

    // Template-name context β€” Optional<U> is a different specialization
    template<typename U>
    Optional(Optional<U>&& other);         // converting constructor

    // Useful for factory methods returning a different specialization
    template<typename U>
    Optional<U> transform(U(*fn)(const T&)) const;
};

This asymmetry is load-bearing: it lets the class refer to itself without repeating template arguments (reducing maintenance burden) while still allowing cross-specialization operations when needed.

CRTP: Why the Injected Name is Not Derived

In CRTP the base class template receives the derived class as a template parameter. A common source of confusion is thinking the injected class name of the base somehow refers to Derived. It does not β€” the injected name of Base<Derived> is Base<Derived>, which means the base.

cpp
template<typename Derived>
class Cloneable {
public:
    // 'Cloneable' here is the injected name = Cloneable<Derived>
    // NOT Derived β€” you must cast explicitly
    Derived* clone() const {
        return new Derived(static_cast<const Derived&>(*this));
    }

    // Wrong β€” returns a Cloneable<Derived>*, slicing occurs at the call site
    // Cloneable* bad_clone() const { return new Cloneable(*this); }
};

class Document : public Cloneable<Document> {
public:
    Document(const Document&) = default;
    std::string text;
};

Inside Cloneable<Derived>, Derived is a template parameter β€” a dependent name whose concrete type is unknown until instantiation. The compiler cannot inspect Derived's members during template definition. This is why CRTP helper methods must accept or return Derived via explicit casts rather than through the injected name.

The static_cast Pattern Explained

static_cast<Derived&>(*this) is the canonical CRTP down-cast. It is safe because CRTP guarantees (by construction) that the most-derived object is a Derived. The base cannot enforce this β€” it is a convention upheld by the programmer. Some codebases add a compile-time assertion:

cpp
template<typename Derived>
class Base {
protected:
    Base() {
        // Verify at construction that Derived actually inherits from Base<Derived>
        static_assert(std::is_base_of_v<Base<Derived>, Derived>,
            "Derived must inherit from Base<Derived>");
    }

    Derived& self() { return static_cast<Derived&>(*this); }
    const Derived& self() const { return static_cast<const Derived&>(*this); }
};

Friend Declarations and the Injected Name

Friend declarations interact with injected names in two important ways.

From Outside: Naming a Class via Its Injected Name

When a class grants friendship to another class, the name in the friend declaration is looked up in the enclosing scope. The injected class name is not accessible from outside, so friend class Foo; finds the outer (global or namespace-scope) Foo:

cpp
struct Foo {};  // global Foo

struct Bar {
    friend class Foo;   // befriends ::Foo β€” the global one
};

From Inside a Template: Friend Referring to the Injected Name as a Template

When the injected class name of a template appears as the final name in a friend elaborated-type-specifier inside a template, the standard (C++20 [temp.local]/3) says it refers to the template itself, not a specialization:

cpp
template<typename T>
class Handle {
    // Inside Handle<T>, 'Handle' as a template-name in a friend declaration
    // refers to the Handle class template β€” granting friendship to ALL specializations
    template<typename U>
    friend class Handle;   // Handle<U> for every U can access Handle<T>'s private members
};

This pattern arises in smart pointer implementations that allow conversion between specializations (e.g., Handle<Derived> constructing from Handle<Base>).

Friend Declarations of Constructors

Because constructors are named using the injected class name, Foo::Foo in a qualified friend declaration refers to the constructor group, not the class:

cpp
class Key {
    Key() = default;
    friend class Factory;   // Factory can call Key's private constructor
};

class Factory {
public:
    static Key make() { return Key{}; }   // OK β€” Factory is a friend of Key
};

Dependent Base Class Lookup and the Template Keyword

When a class template inherits from a dependent base (one whose type depends on a template parameter), the base's injected class name is not found by unqualified lookup inside the derived class body. The standard mandates two-phase lookup: unqualified names are resolved at template definition time, before the base is known.

cpp
template<typename T>
struct Policy {
    void apply();
    template<typename U>
    void convert(U);
};

template<typename T>
struct Worker : Policy<T> {
    void run() {
        // apply();                      // ERROR: unqualified lookup, not found in dependent base
        this->apply();                   // OK: makes 'apply' dependent β€” found at instantiation
        Policy<T>::apply();              // OK: qualified lookup into the base

        // Calling a member template in a dependent base requires 'template'
        // this->convert<int>(42);       // PARSE ERROR: '<' treated as less-than
        this->template convert<int>(42); // OK: 'template' disambiguates '<'
    }
};

The template keyword after ., ->, or :: tells the parser that the following name is a template, so < opens a template-argument-list rather than being parsed as less-than. This requirement applies any time a member template is accessed through a dependent object or scope.

When Injected Names from the Base Are Visible

If the base is non-dependent (its type is fully known at definition time), unqualified lookup does find the base's injected class name:

cpp
struct FixedBase {
    using size_type = std::size_t;
};

template<typename T>
struct Container : FixedBase {
    size_type capacity;   // OK: FixedBase is non-dependent; size_type is found
};

The rule is simple: non-dependent bases are searched during phase 1 (definition time); dependent bases are only searched during phase 2 (instantiation time), and only for dependent names.

C++17 CTAD and the Injected Class Name

Class Template Argument Deduction (CTAD), introduced in C++17, lets you omit template arguments when constructing objects:

cpp
std::pair p{1, 2.0};          // deduces std::pair<int, double>
std::vector v{1, 2, 3};       // deduces std::vector<int>

Inside the implementation of std::vector, the injected class name vector continues to mean vector<T, Allocator> β€” the current instantiation. CTAD operates at the call site, not inside the class body, so the injected name semantics are unaffected.

Deduction Guides

Deduction guides are function-like declarations written outside the class body that tell the compiler how to deduce template arguments. Because they are outside the class, the name used is the template-name (not the injected class name):

cpp
template<typename T>
class Stack {
public:
    Stack(std::initializer_list<T> init);
};

// Deduction guide β€” 'Stack' here is the template-name, not the injected name
template<typename T>
Stack(std::initializer_list<T>) -> Stack<T>;

Stack s = {1, 2, 3};   // deduces Stack<int>

The C++17 standard explicitly states that a template-name in a deduction guide shall not be an injected class name. This prevents a deduction guide from being written inside the class body where only the injected name (current instantiation) would be available.

Implicit Deduction Candidates from Constructors

When CTAD runs, the compiler synthesizes a set of hypothetical function templates, one per constructor. For each constructor of C<T...>, it synthesizes a function template returning C<T...>. These synthesized candidates use the class template β€” not the injected class name β€” in their return type, but the practical effect is that deduction works as expected:

cpp
template<typename T>
class Box {
public:
    Box(T value);                   // synthesized candidate: auto(T) -> Box<T>
    Box(const Box& other) = default; // synthesized: auto(const Box<T>&) -> Box<T>
};

Box b1{42};       // deduces Box<int>  via synthesized candidate from Box(T)
Box b2{b1};       // deduces Box<int>  via copy deduction candidate

The copy deduction candidate is derived from a hypothetical constructor Box(Box) (using the injected name in the parameter, which means Box<T>). This ensures that Box b2 = b1; deduces Box<int> rather than failing.

Qualified Lookup: Foo::Foo Names the Constructor

From outside a class, the injected class name is not accessible as an unqualified name β€” you must use the full class name or a qualified name. Within a qualified name, Foo::Foo (repeated) has a special meaning: it names the set of constructors:

cpp
class Engine {
public:
    Engine(int cylinders, double displacement);
    Engine(const Engine&) = default;
};

// Pointer-to-constructor is not legal in C++, but constructor can be named
// for placement new, explicit constructor calls, or reflection (C++26):
Engine* p = ::new Engine{8, 5.7};   // direct construction

// Using a placement-new expression in a buffer:
alignas(Engine) std::byte buf[sizeof(Engine)];
Engine* pe = new(buf) Engine{4, 2.0};    // uses Engine::Engine(int, double)
pe->~Engine();                            // explicit destructor call: pe->Engine::~Engine()

The destructor notation ~Engine also uses the injected class name, prefixed by ~. Both the constructor group and the destructor are accessed through the injected name when looked up in qualified contexts.

Partial and Explicit Specializations: No Injected Name

Partial specializations and explicit specializations do not inject their own name β€” they are not separate class templates. The injected class name inside a partial specialization is still the primary template's name, with the specialization's own arguments substituted:

cpp
template<typename T>
class Wrapper {};          // primary: injected name is Wrapper (= Wrapper<T>)

template<typename T>
class Wrapper<T*> {        // partial specialization of Wrapper
    // Injected name here is still 'Wrapper', equivalent to Wrapper<T*>
    Wrapper* clone();      // returns Wrapper<T*>*  β€” the current specialization
};

template<>
class Wrapper<void> {      // explicit specialization
    // Injected name: Wrapper, equivalent to Wrapper<void>
    Wrapper() = default;
};

Each specialization gets its own injected class name that corresponds to its own current instantiation, even though they all share the template name Wrapper.

See Also

  • reference/language/class-template β€” template class definitions and instantiation rules
  • reference/language/dependent-names β€” how name lookup differs for dependent vs. non-dependent names
  • reference/language/abstract-classes β€” pure virtual functions and the class hierarchy context where injected names appear