Using-declaration
将其他地方定义的名称引入到当前 using 声明所在的声明区域。有关其他相关声明,请参阅 using enum 和 (C++20 起) using namespace 。
| 
          
           using
          
         
          
           typename
          
         (可选)
         
         
          嵌套名称说明符
         
         
          非限定标识符
          
           ;
          
          | (C++17 前) | ||||||||
| 
          
           using
          
         声明符列表
          
           ;
          
          | (C++17 起) | ||||||||
| 
          
           typename
          
          | - | 当 using 声明从基类向类模板引入成员类型时,可使用关键字 typename 来解析 依赖名称 | 
| nested-name-specifier | - | 由名称和作用域解析运算符 
          ::
         组成的序列,以作用域解析运算符结尾。单个
          ::
         表示全局命名空间 | 
| unqualified-id | - | 一个 标识表达式 | 
| declarator-list | - | 由一或多个声明符组成的逗号分隔列表,形式为 
          
           typename
          
         (可选)
         
         
          nested-name-specifier
         
         
          unqualified-id
         
         。部分或全部声明符后可接省略号
         
          
           ...
          
         
         以指示
         
          包展开 | 
| 目录 | 
说明
using 声明可用于将命名空间成员引入其他命名空间和块作用域,或将基类成员引入派生类定义 ,或将 枚举项 引入命名空间、块及类作用域 (C++20 起) 。
| 具有多个 using 声明符的 using 声明等价于对应的单 using 声明符序列的 using 声明。 | (since C++17) | 
在命名空间与块作用域中
Using-declarations 将其他命名空间的成员引入当前命名空间或块作用域。
#include <iostream> #include <string> using std::string; int main() { string str = "示例"; using std::cout; cout << str; }
参见 namespace 获取详细信息。
在类定义中
using 声明将基类的成员引入派生类定义中,例如将基类的受保护成员暴露为派生类的公开成员。这种情况下, 嵌套名限定符 必须指定当前定义类的某个基类。如果该名称是基类中重载成员函数的名称,则会引入基类中所有同名成员函数。若派生类已存在具有相同名称、参数列表和限定条件的成员,则派生类成员将隐藏或覆盖(而非冲突)从基类引入的成员。
#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 是受保护的 typedef int value_type; }; struct D : B { using B::m; // D::m 是公开的 using B::value_type; // D::value_type 是公开的 using B::f; void f(int) override { std::cout << "D::f\n"; } // D::f(int) 重写 B::f(int) using B::g; void g(int) { std::cout << "D::g\n"; } // g(int) 和 g(char) 均可见 using B::h; void h(int) { std::cout << "D::h\n"; } // D::h(int) 隐藏 B::h(int) }; int main() { D d; B& b = d; // b.m = 2; // 错误:B::m 是受保护的 d.m = 1; // 受保护的 B::m 可作为公开的 D::m 访问 b.f(1); // 调用派生类的 f() d.f(1); // 调用派生类的 f() std::cout << "----------\n"; d.g(1); // 调用派生类的 g(int) d.g('a'); // 调用基类的 g(char),通过 using B::g 暴露 std::cout << "----------\n"; b.h(1); // 调用基类的 h() d.h(1); // 调用派生类的 h() }
输出:
D::f D::f ---------- D::g B::g ---------- B::h D::h
| 继承构造函数若 using 声明 引用的是正在定义的类的直接基类的构造函数(例如 using Base :: Base ; ),则该基类的所有构造函数(忽略成员访问)在对派生类进行初始化时都会在重载决议中可见。 若重载决议选择了继承的构造函数,且该构造函数在用于构造对应基类对象时可访问,则它可被访问:引入该构造函数的 using 声明的可访问性被忽略。 
          若在初始化此类派生类的对象时,重载决议选择了某个继承的构造函数,则继承该构造函数的
           struct B1 { B1(int, ...) {} }; struct B2 { B2(double) {} }; int get(); struct D1 : B1 { using B1::B1; // 继承 B1(int, ...) int x; int y = get(); }; void test() { D1 d(2, 3, 4); // OK:通过调用 B1(2, 3, 4) 初始化 B1, // 然后 d.x 默认初始化(不执行初始化), // 然后 d.y 通过调用 get() 初始化 D1 e; // 错误:D1 没有默认构造函数 } struct D2 : B2 { using B2::B2; // 继承 B2(double) B1 b; }; D2 f(1.0); // 错误:B1 没有默认构造函数 struct W { W(int); }; struct X : virtual W { using W::W; // 继承 W(int) X() = delete; }; struct Y : X { using X::X; }; struct Z : Y, virtual W { using Y::Y; }; Z z(0); // OK:Y 的初始化不会调用 X 的默认构造函数 
          若
           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”,因为 V 子对象 // 不作为 B 的一部分初始化 //(V 子对象作为 C 的一部分初始化, // 因为 “c” 是最終派生对象) } 
          若构造函数从多个
           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); // 非良构:从不同的 B 基类子对象继承构造函数 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:只有一个 B 子对象。 // 这会初始化虚基类 B, // 它初始化基类 A // 然后初始化基类 V1 和 V2 // 如同通过默认的默认构造函数 
          与任何其他非静态成员函数的 using 声明一样,若继承的构造函数与
           struct B1 { B1(int); }<span class=" | 
       
      
| 引入作用域枚举项除了其他命名空间的成员和基类的成员之外,using 声明还可以将 枚举 的枚举项引入命名空间、块作用域和类作用域。 using 声明也可用于无作用域枚举项。 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 | (since C++20) | 
注释
仅通过 using 声明明确提及的名称会被转入声明作用域:特别地,当枚举类型名被 using 声明时,其枚举项不会被转入。
using 声明不能引用命名空间 、作用域枚举项 (C++20 前) 、基类的析构函数或用户定义转换函数的成员模板特化。
using声明不能命名成员模板特化(语法不允许 模板ID ):
struct B { template<class T> void f(); }; struct D : B { using B::f; // 正确:声明模板 // using B::f<int>; // 错误:声明模板特化 void g() { f<int>(); } };
       using声明不能用于将依赖成员模板的名称作为
       
        模板名称
       
       引入(不允许对
       
        依赖名称
       
       使用
       
        template
       
       消歧符)。
      
template<class X> struct B { template<class T> void f(T); }; template<class Y> struct D : B<Y> { // using B<Y>::template f; // 错误:不允许使用消歧符 using B<Y>::f; // 编译通过,但 f 不是模板名称 void g() { // f<int>(0); // 错误:f 未被识别为模板名称, // 因此 < 不会开始模板参数列表 f(0); // 正确 } };
如果 using 声明将基类赋值运算符引入派生类,且其签名恰好与派生类的复制赋值或移动赋值运算符匹配,则该运算符会被派生类隐式声明的复制/移动赋值运算符隐藏。 同样适用于继承基类构造函数的 using 声明,当该构造函数恰好与派生类复制/移动构造函数匹配时 (C++11 起) 。
| 继承构造函数的语义曾通过 针对C++11的缺陷报告 进行追溯性修改。早期版本中,继承构造函数声明会导致一组合成构造函数声明被注入派生类,这会引起冗余的参数拷贝/移动操作,与某些形式的SFINAE存在交互问题,且在部分主流ABI中无法实现。旧版本编译器可能仍沿用早期语义。 
 | (C++11 起) | 
| 包展开 在 using 声明中使得无需递归即可形成暴露可变参数基类重载成员的类: 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;} }; } | (since C++17) | 
| 功能测试宏 | 值 | 标准 | 功能特性 | 
|---|---|---|---|
| 
           __cpp_inheriting_constructors
           | 
           200802L
           | (C++11) | 继承构造函数 | 
| 
           201511L
           | (C++17) (DR11) | 重述继承构造函数 | |
| 
           __cpp_variadic_using
           | 
           201611L
           | (C++17) | 参数包展开
         
         在 
          using
         声明中 | 
关键词
缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的C++标准。
| 缺陷报告 | 应用于 | 发布时的行为 | 正确行为 | 
|---|---|---|---|
| CWG 258 | C++98 | 派生类的非const成员函数可以 重写和/或隐藏其基类的const成员函数 | 重写和隐藏还需要 cv限定符相同 | 
| CWG 1738 | C++11 | 未明确是否允许 显式实例化或显式特化 继承构造函数模板的特化 | 禁止 | 
| CWG 2504 | C++11 | 从虚基类继承构造函数 的行为不明确 | 予以明确 | 
| P0136R1 | C++11 | 继承构造函数声明会注入 额外的构造函数到派生类中 | 使基类构造函数 可通过名称查找找到 | 
参考文献
- C++23 标准 (ISO/IEC 14882:2024):
- 
         - 
           9.9
           using声明 [namespace.udecl]
 
- 
           9.9
           
- C++20 标准 (ISO/IEC 14882:2020):
- 
         - 
           9.9
           using声明 [namespace.udecl]
 
- 
           9.9
           
- C++17 标准 (ISO/IEC 14882:2017):
- 
         - 
           10.3.3
           using声明 [namespace.udecl]
 
- 
           10.3.3
           
- C++14 标准 (ISO/IEC 14882:2014):
- 
         - 
           7.3.3
           using声明 [namespace.udecl]
 
- 
           7.3.3
           
- C++11 标准 (ISO/IEC 14882:2011):
- 
         - 
           7.3.3
           using声明 [namespace.udecl]
 
- 
           7.3.3
           
- C++03 标准 (ISO/IEC 14882:2003):
- 
         - 
           7.3.3
           using声明 [namespace.udecl]
 
- 
           7.3.3
           
- C++98 标准 (ISO/IEC 14882:1998):
- 
         - 
           7.3.3
           using声明 [namespace.udecl]
 
- 
           7.3.3