命名空间
变体
操作

Using-declaration

来自 cppreference.cn
< cpp‎ | language
 
 
C++ 语言
通用主题
流程控制
条件执行语句
if
迭代语句(循环)
for
range-for (C++11)
跳转语句
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (直到 C++17*)
noexcept 说明符 (C++11)
异常
命名空间
类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储期说明符
初始化
 
 

引入一个在其他地方定义的名称到此 using-declaration 出现的声明区域。参阅 using enum(自 C++20 起)using namespace 以了解其他相关声明。

using typename(可选) nested-name-specifier unqualified-id ; (直到 C++17)
using declarator-list ; (自 C++17 起)
typename - 关键字 typename 可以根据需要用于解析 依赖名称,当 using-declaration 将基类的成员类型引入类模板时
nested-name-specifier - 名称和作用域解析运算符 :: 的序列,以作用域解析运算符结尾。单个 :: 指的是全局命名空间。
unqualified-id - 一个 id-expression
declarator-list - 一个或多个声明符的逗号分隔列表,格式为 typename(可选) nested-name-specifier unqualified-id。某些或所有声明符后面可以跟一个省略号 ... 以指示 包展开

目录

[编辑] 解释

Using-declaration 可以用于将命名空间成员引入其他命名空间和块作用域,或将基类成员引入派生类定义,或将 枚举器 引入命名空间、块和类作用域(自 C++20 起)

具有多个 using-declarator 的 using-declaration 等效于具有一个 using-declarator 的相应 using-declaration 序列。

(自 C++17 起)

[编辑] 在命名空间和块作用域中

Using-declaration 将另一个命名空间的成员引入当前命名空间或块作用域。

#include <iostream>
#include <string>
 
using std::string;
 
int main()
{
    string str = "Example";
    using std::cout;
    cout << str;
}

有关详细信息,请参阅 namespace

[编辑] 在类定义中

Using-declaration 将基类的成员引入派生类定义,例如将基类的受保护成员公开为派生类的公共成员。在这种情况下,nested-name-specifier 必须命名正在定义的类的基类。如果该名称是基类的重载成员函数的名称,则会引入具有该名称的所有基类成员函数。如果派生类已经具有具有相同名称、参数列表和限定符的成员,则派生类成员会隐藏或覆盖(不冲突)从基类引入的成员。

#include <iostream>
 
struct B
{
    virtual void f(int) { std::cout << "B::f\n"; }
    void g(char)        { std::cout << "B::g\n"; }
    void h(int)         { std::cout << "B::h\n"; }
protected:
    int m; // B::m is protected
    typedef int value_type;
};
 
struct D : B
{
    using B::m;          // D::m is public
    using B::value_type; // D::value_type is public
 
    using B::f;
    void f(int) override { std::cout << "D::f\n"; } // D::f(int) overrides B::f(int)
 
    using B::g;
    void g(int) { std::cout << "D::g\n"; } // both g(int) and g(char) are visible
 
    using B::h;
    void h(int) { std::cout << "D::h\n"; } // D::h(int) hides B::h(int)
};
 
int main()
{
    D d;
    B& b = d;
 
//  b.m = 2;  // Error: B::m is protected
    d.m = 1;  // protected B::m is accessible as public D::m
 
    b.f(1);   // calls derived f()
    d.f(1);   // calls derived f()
    std::cout << "----------\n";
 
    d.g(1);   // calls derived g(int)
    d.g('a'); // calls base g(char), exposed via using B::g;
    std::cout << "----------\n";
 
    b.h(1);   // calls base h()
    d.h(1);   // calls derived h()
}

输出

D::f
D::f
----------
D::g
B::g
----------
B::h
D::h

继承构造函数

如果 using-declaration 引用正在定义的类的直接基类的构造函数(例如 using Base::Base;),则该基类的所有构造函数(忽略成员访问)在初始化派生类时都可见以进行重载解析。

如果重载解析选择了一个继承的构造函数,则当用于构造相应基类的对象时,它是可访问的:引入它的 using-declaration 的可访问性将被忽略。

如果重载解析在初始化此类派生类的对象时选择了一个继承的构造函数,则使用继承的构造函数初始化从中继承构造函数的 Base 子对象,并且 Derived 的所有其他基类和成员都如同通过默认的默认构造函数进行初始化(如果提供了默认成员初始化器,则使用它们,否则进行默认初始化)。整个初始化被视为单个函数调用:继承的构造函数的参数的初始化 先于 派生对象的任何基类或成员的初始化。

struct B1 { B1(int, ...) {} };
struct B2 { B2(double)   {} };
 
int get();
 
struct D1 : B1
{
    using B1::B1; // inherits B1(int, ...)
    int x;
    int y = get();
};
 
void test()
{
    D1 d(2, 3, 4); // OK: B1 is initialized by calling B1(2, 3, 4),
                   // then d.x is default-initialized (no initialization is performed),
                   // then d.y is initialized by calling get()
 
    D1 e;          // Error: D1 has no default constructor
}
 
struct D2 : B2
{
    using B2::B2; // inherits B2(double)
    B1 b;
};
 
D2 f(1.0); // error: B1 has no default constructor
struct W { W(int); };
 
struct X : virtual W
{
    using W::W; // inherits W(int)
    X() = delete;
};
 
struct Y : X
{
    using X::X;
};
 
struct Z : Y, virtual W
{
    using Y::Y;
};
 
Z z(0); // OK: initialization of Y does not invoke default constructor of X

如果 Base 基类子对象不作为 Derived 对象的一部分进行初始化(即,BaseDerived虚基类,并且 Derived 对象不是 最派生对象),则省略继承的构造函数的调用,包括任何参数的求值

struct V
{
    V() = default;
    V(int);
};
 
struct Q { Q(); };
 
struct A : virtual V, Q
{
    using V::V;
    A() = delete;
};
 
int bar() { return 42; }
 
struct B : A
{
    B() : A(bar()) {} // OK
};
 
struct C : B {};
 
void foo()
{
    C c; // “bar” is not invoked, because the V subobject
         // is not initialized as part of B
         // (the V subobject is initialized as part of C,
         //  because “c” is the most derived object)
}

如果构造函数是从 Base 类型的多个基类子对象继承的,则程序是非良构的,类似于多重继承的非静态成员函数

struct A { A(int); };
struct B : A { using A::A; };
struct C1 : B { using B::B; };
struct C2 : B { using B::B; };
 
struct D1 : C1, C2
{
    using C1::C1;
    using C2::C2;
};
D1 d1(0); // ill-formed: constructor inherited from different B base subobjects
 
struct V1 : virtual B { using B::B; };
struct V2 : virtual B { using B::B; };
 
struct D2 : V1, V2
{
    using V1::V1;
    using V2::V2;
};
D2 d2(0); // OK: there is only one B subobject.
          // This initializes the virtual B base class,
          //   which initializes the A base class
          // then initializes the V1 and V2 base classes
          //   as if by a defaulted default constructor

与任何其他非静态成员函数的 using-declaration 一样,如果继承的构造函数与 Derived 的构造函数之一的签名匹配,则它会被 Derived 中找到的版本隐藏。如果 Base 的继承构造函数之一恰好具有与 Derived 的复制/移动构造函数匹配的签名,则它不会阻止隐式生成 Derived 复制/移动构造函数(然后隐藏继承的版本,类似于 using operator=)。

struct B1 { B1(int); };
struct B2 { B2(int); };
 
struct D2 : B1, B2
{
    using B1::B1;
    using B2::B2;
 
    D2(int); // OK: D2::D2(int) hides both B1::B1(int) and B2::B2(int)
};
D2 d2(0);    // calls D2::D2(int)

模板类 中,如果 using-declaration 引用 依赖名称,则如果 nested-name-specifier 具有与 unqualified-id 相同的终端名称,则它被视为命名构造函数。

template<class T>
struct A : T
{
    using T::T; // OK, inherits constructors of T
};
 
template<class T, class U>
struct B : T, A<U>
{
    using A<U>::A; // OK, inherits constructors of A<U>
    using T::A;    // does not inherit constructor of T
                   // even though T may be a specialization of A<>
};
(自 C++11 起)


引入作用域枚举器

除了另一个命名空间的成员和基类的成员之外,using-declaration 还可以将 枚举 的枚举器引入命名空间、块和类作用域。

using-declaration 也可以与非作用域枚举器一起使用。

enum class button { up, down };
 
struct S
{
    using button::up;
    button b = up; // OK
};
 
using button::down;
constexpr button non_up = down; // OK
 
constexpr auto get_button(bool is_up)
{
    using button::up, button::down;
    return is_up ? up : down; // OK
}
 
enum unscoped { val };
using unscoped::val; // OK, though needless
(自 C++20 起)

[编辑] 注释

只有 using-declaration 中显式提及的名称才会被转移到声明作用域:特别是,当枚举类型名称是 using-declared 时,枚举器不会被转移。

using-declaration 不能引用命名空间,作用域枚举器(直到 C++20),基类的析构函数或用户定义的转换函数的成员模板的特化。

using-declaration 不能命名成员模板特化(语法不允许 template-id

struct B
{
    template<class T>
    void f();
};
 
struct D : B
{
    using B::f;      // OK: names a template
//  using B::f<int>; // Error: names a template specialization
 
    void g() { f<int>(); }
};

using-declaration 也不能用于将依赖成员模板的名称作为 template-name 引入(不允许使用 依赖名称template 消歧义符)。

template<class X>
struct B
{
    template<class T>
    void f(T);
};
 
template<class Y>
struct D : B<Y>
{
//  using B<Y>::template f; // Error: disambiguator not allowed
    using B<Y>::f;          // compiles, but f is not a template-name
 
    void g()
    {
//      f<int>(0);          // Error: f is not known to be a template name,
                            // so < does not start a template argument list
        f(0);               // OK
    }   
};

如果 using-declaration 将基类赋值运算符引入派生类,而其签名恰好与派生类的复制赋值或移动赋值运算符匹配,则该运算符会被派生类的隐式声明的复制/移动赋值运算符隐藏。对于继承基类构造函数的 using-declaration,如果恰好与派生类复制/移动构造函数匹配,则同样适用(自 C++11 起)

继承构造函数的语义被 针对 C++11 的缺陷报告 追溯性地更改。以前,继承构造函数声明导致一组合成的构造函数声明被注入到派生类中,这导致了冗余的参数复制/移动,与某些形式的 SFINAE 存在问题交互,并且在某些情况下在主要的 ABI 上可能无法实现。较旧的编译器可能仍然实现以前的语义。

旧的继承构造函数语义

如果 using-declaration 引用正在定义的类的直接基类的构造函数(例如 using Base::Base;),则根据以下规则继承该基类的构造函数

1) 一组候选继承构造函数由以下组成
a) 基类的所有非模板构造函数 (在省略省略号参数(如果有)之后)(自 C++14 起)
b) 对于每个带有默认参数或省略号参数的构造函数,通过删除省略号并从参数列表的末尾逐个省略默认参数而形成的所有构造函数签名
c) 基类的所有构造函数模板 (在省略省略号参数(如果有)之后)(自 C++14 起)
d) 对于每个带有默认参数或省略号的构造函数模板,通过删除省略号并从参数列表的末尾逐个省略默认参数而形成的所有构造函数签名
2) 所有不是默认构造函数或复制/移动构造函数且签名与派生类中的用户定义构造函数不匹配的候选继承构造函数,都在派生类中隐式声明。默认参数不会被继承
struct B1
{
    B1(int);
};
 
struct D1 : B1
{
    using B1::B1;
 
    // The set of candidate inherited constructors is 
    // 1. B1(const B1&)
    // 2. B1(B1&&)
    // 3. B1(int)
 
    // D1 has the following constructors:
    // 1. D1() = delete
    // 2. D1(const D1&) 
    // 3. D1(D1&&)
    // 4. D1(int) <- inherited
};
 
struct B2
{
    B2(int = 13, int = 42);
};
 
struct D2 : B2
{
    using B2::B2;
 
    // The set of candidate inherited constructors is
    // 1. B2(const B2&)
    // 2. B2(B2&&)
    // 3. B2(int = 13, int = 42)
    // 4. B2(int = 13)
    // 5. B2()
 
    // D2 has the following constructors:
    // 1. D2()
    // 2. D2(const D2&)
    // 3. D2(D2&&)
    // 4. D2(int, int) <- inherited
    // 5. D2(int) <- inherited
};

继承的构造函数等效于用户定义的构造函数,它们具有空的主体和由单个 nested-name-specifier 组成的 成员初始化列表,该列表将其所有参数转发到基类构造函数。

它具有与相应基类构造函数相同的 访问权限。如果用户定义的构造函数满足 constexpr 构造函数要求,则它是 constexpr。如果相应的基类构造函数被删除,或者如果默认的默认构造函数将被删除(除了正在继承其构造函数的基类的构造不计算在内),则它将被删除。继承的构造函数不能显式实例化或显式特化。

如果两个 using-declaration 继承了具有相同签名的构造函数(来自两个直接基类),则程序是非良构的。

继承的构造函数模板不应 显式实例化显式特化

(自 C++11 起)

包展开 在 using-declaration 中使得可以形成一个类,该类公开可变基类的重载成员,而无需递归

template<typename... Ts>
struct Overloader : Ts...
{
    using Ts::operator()...; // exposes operator() from every base
};
 
template<typename... T>
Overloader(T...) -> Overloader<T...>; // C++17 deduction guide, not needed in C++20
 
int main()
{
    auto o = Overloader{ [] (auto const& a) {std::cout << a;},
                         [] (float f) {std::cout << std::setprecision(3) << f;} };
}
(自 C++17 起)
特性测试宏 Std 特性
__cpp_inheriting_constructors 200802L (C++11) 继承构造函数
201511L (C++17)
(DR11)
重写继承构造函数
__cpp_variadic_using 201611L (C++17) 包展开using-declaration 中

[编辑] 关键字

using

[编辑] 缺陷报告

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

DR 应用于 已发布行为 正确行为
CWG 258 C++98 派生类的非常量成员函数可以
覆盖和/或隐藏其基类的常量成员函数
覆盖和隐藏也需要
cv-限定符相同
CWG 1738 C++11 不清楚是否允许
显式实例化或显式特化
继承构造函数模板的特化
禁止
CWG 2504 C++11 继承构造函数的行为
来自虚基类的情况不清楚
已明确
P0136R1 C++11 继承构造函数声明注入
派生类中的附加构造函数
导致基类构造函数
通过名称查找找到
  1. 参考文献

[编辑] 参考文献

  • C++23 标准 (ISO/IEC 14882:2024)
  • 9.9 using 声明 [namespace.udecl]
  • C++20 标准 (ISO/IEC 14882:2020)
  • 9.9 using 声明 [namespace.udecl]
  • C++17 标准 (ISO/IEC 14882:2017)
  • 10.3.3 using 声明 [namespace.udecl]
  • C++14 标准 (ISO/IEC 14882:2014)
  • 7.3.3 using 声明 [namespace.udecl]
  • C++11 标准 (ISO/IEC 14882:2011)
  • 7.3.3 using 声明 [namespace.udecl]
  • C++03 标准 (ISO/IEC 14882:2003)
  • 7.3.3 using 声明 [namespace.udecl]
  • C++98 标准 (ISO/IEC 14882:1998)
  • 7.3.3 using 声明 [namespace.udecl]