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++98Within 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:
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:
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.
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:
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:
Ring<int>* r = new Ring<int>(); // must be explicit outside the class bodyCRTP 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:
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:
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:
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:
// 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:
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:
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:
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:
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:
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:
-
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:
VecmeansVec<T, Alloc>. -
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.
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.
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:
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:
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:
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:
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.
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:
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:
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):
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:
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 candidateThe 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:
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:
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 rulesreference/language/dependent-namesβ how name lookup differs for dependent vs. non-dependent namesreference/language/abstract-classesβ pure virtual functions and the class hierarchy context where injected names appear