在线文档教程
C++
语言 | Language

Using-declaration

使用-声明

将在其他地方定义的名称引入此“使用-声明”出现的声明区域中。

using typename(optional) nested-name-specifier unqualified-id ;(until C++17)
using declarator-list ;(since C++17)

nested-name-specifier-a sequence of names and scope resolution operators ::, ending with a scope resolution operator. A single :: refers to the global namespace.
unqualified-id-an id-expression
typename-the keyword typename may be used as necessary to resolve dependent names, when the using-declaration introduces a member type from a base class into a class template
declarator-list-comma-separated list of one or more declarators of the typename(optional) nested-name-specifier unqualified-id. Some or all of the declarators may be followed by an ellipsis ... to indicate pack expansion

解释

使用-声明可以用于将命名空间成员引入其他名称空间和块作用域,或者将基类成员引入派生类定义。

A using-declaration with more than one using-declarator is equivalent to a corresponding sequence of using-declarations with one using-declarator.(since C++17)

在命名空间和块范围中

使用-声明将另一个名称空间的成员引入当前命名空间或块范围。

二次

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

二次

见命名空间关于细节。

在类中定义

使用-声明将基类成员引入派生类定义,例如将基的受保护成员公开为派生类的公共成员。在这种情况下,嵌套名称说明符必须为正在定义的基类命名.。如果名称是基类的重载成员函数的名称,则会引入具有该名称的所有基类成员函数。如果派生类已经具有具有相同名称、参数列表和限定条件的成员,则派生类成员隐藏或重写%28%27T与从基类引入的成员%29冲突。

二次

#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) { 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 // as members of D 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() d.g(1 // calls derived g(int) d.g('a' // calls base g(char) b.h(1 // calls base h() d.h(1 // calls derived h() }

二次

产出:

二次

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

二次

继承构造器

If the using-declaration refers to a constructor of a direct base of the class being defined (e.g. using Base::Base;), constructors of that base class are inherited, according to the following rules: 1) A set of candidate inheriting constructors is composed of a) All non-template constructors of the base class (after omitting ellipsis parameters, if any) (since C++14) b) For each constructor with default arguments or the ellipsis parameter, all constructor signatures that are formed by dropping the ellipsis and omitting default arguments from the ends of argument lists one by one c) All constructor templates of the base class (after omitting ellipsis parameters, if any) (since C++14) d) For each constructor template with default arguments or the ellipsis, all constructor signatures that are formed by dropping the ellipsis and omitting default arguments from the ends of argument lists one by one 2) All candidate inherited constructors that aren't the default constructor or the copy/move constructor and whose signatures do not match user-defined constructors in the derived class, are implicitly declared in the derived class. The default parameters are not inherited: 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 }; The inherited constructors are equivalent to user-defined constructors with an empty body and with a member initializer list consisting of a single nested-name-specifier, which forwards all of its arguments to the base class constructor. It has the same access as the corresponding base constructor. It is constexpr if the user-defined constructor would have satisfied constexpr constructor requirements. It is deleted if the corresponding base constructor is deleted or if a defaulted default constructor would be deleted (except that the construction of the base whose constructor is being inherited doesn't count). An inheriting constructor cannot be explicitly instantiated or explicitly specialized. If two using-declarations inherit the constructor with the same signature (from two direct base classes), the program is ill-formed.(since C++11)(until C++17)
If the using-declaration refers to a constructor of a direct base of the class being defined (e.g. using Base::Base;), all constructors of that base (ignoring member access) are made visible to overload resolution when initializing the derived class. If overload resolution selects an inherited constructor, it is accessible if it would be accessible when used to construct an object of the corresponding base class: the accessibility of the using-declaration that introduced it is ignored. If overload resolution selects one of the inherited constructors when initializing an object of such derived class, then the Base subobject from which the constructor was inherited is initialized using the inherited constructor, and all other bases and members of Derived are initialized as if by the defaulted default constructor (default member initializers are used if provided, otherwise default initialization takes place). The entire initialization is treated as a single function call: initialization of the parameters of the inherited constructor is sequenced-before initialization of any base or member of the derived object. 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 If the constructor was inherited from multiple base class subobjects of type B, the program is ill-formed, similar to multiply-inherited non-static member functions: 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 As with using-declarations for any other non-static member functions, if an inherited constructor matches the signature of one of the constructors of Derived, it is hidden from lookup by the version found in Derived. If one of the inherited constructors of Base happens to have the signature that matches a copy/move constructor of the Derived, it does not prevent implicit generation of Derived copy/move constructor (which then hides the inherited version, similar to 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)(since C++17)

注记

只有在Using-声明中显式提到的名称才被转移到声明范围中:特别是当枚举类型名称使用-声明时,枚举数不被传输。

使用声明不能引用名称空间、范围枚举数、基类的析构函数或用户定义转换函数的成员模板的专门化。

使用-声明可以引用成员模板,但不能引用成员模板专门化%28模板-id不允许%29。

二次

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>( } };

二次

使用-声明还可以%27T引用依赖成员模板%28模板消歧器相依名称不允许%29。

二次

template<class X> struct B { template<class T> void f( }; template<class Y> struct D : B<Y> { // using B<Y>::template f; // Error: disambiguator not allowed using B<Y>::f; // compiles, but declares f as non-type non-template void g() { f<int>( } // Error here };

二次

如果使用声明将基类赋值运算符带入派生类,其签名恰好与派生类%27s复制赋值或移动赋值运算符相匹配,则派生类的隐式声明复制/移动赋值操作符隐藏了该运算符。同样的情况也适用于继承基类构造函数的自C++17%29以来与派生类复制/移动构造函数%28匹配的使用声明。

Pack expansions in using-declarations make it possible to form a class that exposes overloaded members of variadic bases without recursion: template struct Overloader : Ts... { using Ts::operator()...; // exposes operator() from every base }; template Overloader(T...) -> Overloader; // C++17 deduction guide int main() { auto o = Overloader{ {std::cout << a;}, {std::cout << std::setprecision(3) << f;} }; }(since C++17)

© cppreference.com

在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。

http://en.cppreference.com/w/cpp/language/Win[医]声明