Using-declaration
引入一个在其他地方定义的名称到此 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 的可访问性将被忽略。 如果重载解析在初始化此类派生类的对象时选择了一个继承的构造函数,则使用继承的构造函数初始化从中继承构造函数的 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 引用 依赖名称,则如果 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 上可能无法实现。较旧的编译器可能仍然实现以前的语义。
|
(自 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 中 |
[编辑] 关键字
[编辑] 缺陷报告
以下行为更改的缺陷报告被追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
CWG 258 | C++98 | 派生类的非常量成员函数可以 覆盖和/或隐藏其基类的常量成员函数 |
覆盖和隐藏也需要 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