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
};

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