使用声明
在使用声明出现的声明区域中引入一个在其他地方定义的名称。请参阅 using enum 和 (自 C++20 起)using namespace 以获取其他相关声明。
using typename (可选) 嵌套命名空间说明符 非限定标识符 ; |
(直到 C++17) | ||||||||
using 声明符列表 ; |
(自 C++17 起) | ||||||||
typename
|
- | 关键字 typename 可用于解析 依赖名称,当使用声明从基类中引入一个成员类型到类模板中时。 |
嵌套命名空间说明符 | - | 名称和作用域解析运算符 :: 的序列,以作用域解析运算符结束。单个 :: 指向全局命名空间。 |
非限定标识符 | - | 一个 id-表达式 |
声明符列表 | - | 一个或多个 typename (可选) 嵌套命名空间说明符 非限定标识符 声明符的逗号分隔列表。某些或所有声明符后面可以是省略号 ... 以指示 包展开 |
内容 |
[编辑] 解释
使用声明可用于将命名空间成员引入其他命名空间和块作用域,或将基类成员引入派生类定义,或者将 枚举器 引入命名空间、块和类作用域(自 C++20 起).
具有多个使用声明符的使用声明等效于具有一个使用声明符的相应使用声明序列。 |
(自 C++17 起) |
[编辑] 在命名空间和块作用域中
使用声明 将另一个命名空间的成员引入当前命名空间或块作用域。
#include <iostream> #include <string> using std::string; int main() { string str = "Example"; using std::cout; cout << str; }
有关详细信息,请参阅 命名空间。
[编辑] 在类定义中
使用声明将基类的成员引入派生类定义,例如,将基类的受保护成员公开为派生类的公有成员。在这种情况下,嵌套命名空间说明符 必须命名正在定义的类的一个基类。如果名称是基类中重载成员函数的名称,则所有具有该名称的基类成员函数都将被引入。如果派生类已经具有具有相同名称、参数列表和限定符的成员,则派生类成员将隐藏或覆盖 (不会与之冲突) 从基类引入的成员。
#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 的可访问性将被忽略。 如果重载解析在初始化此类派生类对象时选择了继承的构造函数之一,则使用继承的构造函数初始化来自该构造函数的 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 如果 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) } 如果从类型为 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 一样,如果继承的构造函数与 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 引用依赖名称,则如果嵌套名称说明符的终止名称与非限定标识符相同,则它被认为是命名构造函数。 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-declaration 时,枚举器不会被转移。
using-declaration 不能引用命名空间,作用域枚举器(直到 C++20),基类的析构函数或用户定义的转换函数的成员模板特化。
using-declaration 不能命名成员模板特化(模板 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
消歧符是不允许的)。
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 上实现。旧编译器可能仍然实现以前的语义。
|
(从 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++11) (DR) |
重新措辞继承构造函数 | |
__cpp_variadic_using |
201611L | (C++17) | 包扩展在 using -declaration 中 |
[编辑] 关键字
[编辑] 缺陷报告
以下行为改变的缺陷报告被追溯地应用到以前发布的 C++ 标准。
DR | 应用于 | 已发布的行为 | 正确行为 |
---|---|---|---|
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