Namespaces
Variants

Injected-class-name

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

注入类名是指在该类作用域内使用的类本身的非限定名称。

类模板 中,注入类名既可用作引用当前模板的模板名称,也可用作引用当前实例化的类名称。

目录

说明

类作用域 中,当前类的类名或当前类模板的模板名被视为如同是公开成员名称;这被称为 注入类名 。该名称的声明点紧跟在类(模板)定义的开花括号之后。

int X;
struct X
{
    void f()
    {
        X* p;   // 正确:X 是注入类名
        ::X* q; // 错误:名称查找找到变量名,该名称隐藏了结构体名称
    }
};
template<class T>
struct Y
{
    void g()
    {
        Y* p;    // 正确:Y 是注入类名
        Y<T>* q; // 正确:Y 是注入类名,但 Y<T> 不是
    }
};

与其他成员类似,注入类名会被继承。当存在私有或受保护继承时,间接基类的注入类名可能在派生类中变得不可访问。

struct A {};
struct B : private A {};
struct C : public B
{
    A* p;   // 错误:注入类名 A 不可访问
    ::A* q; // 正确,不使用注入类名
};

在类模板中

类模板的注入类名可用作模板名或类型名。

在以下情况下,被注入的类名会被视为类模板自身的模板名称:

否则,它将被视为类型名称,并等价于模板名称后随类模板的模板参数(置于 <> 中)。

template<template<class, class> class>
struct A;
template<class T1, class T2>
struct X
{
    X<T1, T2>* p;   // 正确:X 被视为模板名称
    using a = A<X>; // 正确:X 被视为模板名称
    template<class U1, class U2>
    friend class X; // 正确:X 被视为模板名称
    X* q;           // 正确:X 被视为类型名称,等价于 X<T1, T2>
};

在类 模板特化 偏特化 的作用域内,当注入类名被用作类型名时,它等同于后接类模板特化或偏特化的模板实参(用 <> 括起)的模板名称。

template<>
struct X<void, void>
{
    X* p; // 正确:X 被视为类型名称,等价于 X<void, void>
    template<class, class>
    friend class X; // 正确:X 被视为模板名称(与主模板中相同)
    X<void, void>* q; // 正确:X 被视为模板名称
};
template<class T>
struct X<char, T>
{
    X* p, q; // 正确:X 被视为类型名称,等价于 X<char, T>
    using r = X<int, int>; // 正确:可用于命名其他特化
};

类模板或类模板特化的注入类名在其作用域内既可用作模板名,也可用作类型名。

template<>
class X<int, char>
{
    class B
    {
        X a;            // 表示 X<int, char>
        template<class, class>
        friend class X; // 表示 ::X
    };
};
template<class T>
struct Base
{
    Base* p; // 正确:Base 表示 Base<T>
};
template<class T>
struct Derived : public Base<T*>
{
    typename Derived::Base* p; // 正确:Derived::Base 表示 Derived<T>::Base,
                               // 即 Base<T*>
};
template<class T, template<class> class U = T::template Base>
struct Third {};
Third<Derived<int>> t; // 正确:默认参数使用注入类名作为模板

在某些情况下,查找注入类名的操作可能导致歧义(例如,如果在多个基类中都找到了该名称)。如果所有找到的注入类名都指向同一类模板的特化,且该名称被用作模板名时,该引用指向的是类模板本身而非其特化,此时不会产生歧义。

template<class T>
struct Base {};
template<class T>
struct Derived: Base<int>, Base<char>
{
    typename Derived::Base b;         // 错误:存在歧义
    typename Derived::Base<double> d; // 正确
};

注入类名与构造函数

构造函数没有名称,但在构造函数声明和定义中,其外围类的注入类名被视为构造函数的名称。

在限定名称 C::D 中,若

  • 名称查找不会忽略函数名称,且
  • 在类 C 作用域内查找 D 会找到其注入类名

限定名称始终被视为命名 C 的构造函数。此类名称仅能用于构造函数的声明中(例如在友元构造函数声明、构造函数模板特化、构造函数模板实例化或构造函数定义中) 或用于继承构造函数 (C++11 起)

struct A
{
    A();
    A(int);
    template<class T>
    A(T) {}
};
using A_alias = A;
A::A() {}
A_alias::A(int) {}
template A::A(double);
struct B : A
{
    using A_alias::A;
};
A::A a;         // 错误:A::A 被视为构造函数名称,而非类型
struct A::A a2; // 正确,等同于 'A a2;'
B::A b;         // 正确,等同于 'A b;'

缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的C++标准。

缺陷报告 应用于 发布时行为 正确行为
CWG 1004 C++98 注入类名不能作为
模板模板参数使用
允许,此时它指向类
模板本身
CWG 2637 C++98 整个模板特化标识可以是注入类名 仅模板名称可以