首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Namespaces

命名空间提供了一种在大型项目中防止名称冲突的方法。

在命名空间块中声明的符号被放置在命名范围中,以防止它们在其他作用域中被误认为同名符号。

允许具有相同名称的多个命名空间块。这些块中的所有声明都在指定的作用域中声明。

句法

namespace ns_name { declarations }

(1)

?

inline namespace ns_name { declarations }

(2)

(since C++11)

namespace { declarations }

(3)

?

ns_name::name

(4)

?

using namespace ns_name;

(5)

?

using ns_name::name;

(6)

?

namespace name = qualified-namespace ;

(7)

?

namespace ns_name::name

(8)

(since C++17)

1%29命名命名空间定义名称空间ns[医]名字。

2%29内联命名空间定义名称空间ns[医]名字。ns内部声明[医]名称将在其封闭的命名空间中可见。

3%29未命名命名空间定义它的成员有从他们的申报点到翻译单位结束的潜在范围,并且有内部联动...

4%29命名空间名称%28和类名%29可以显示在范围解析运算符的左侧,作为限定名查找...

5%29使用指令*从无保留的观点出发名称查找在使用指令之后,直到它出现的作用域结束为止,每个名称都来自ns。[医]名称是可见的,就好像它是在最近的封闭命名空间中声明的,其中包含了use-指令和ns。[医]名字。

6%29使用-声明::从命名空间ns生成符号名。[医]可访问的名称不合格查找就好像在同一个类范围、块范围或名称空间中声明了这个使用-声明。

7%29命名空间-别名-定义:使名称成为另一个名称空间的同义词:命名空间别名

8%29嵌套命名空间定义:namespace A::B::C {等于namespace A { namespace B { namespace C {

解释

命名空间

inline(optional) namespace attr(optional) identifier { namespace-body }

?

?

inline

-

if present, makes this an inline namespace (see below). Cannot appear on the extension-namespace-definition if the original-namespace-definition did not use inline

attr(C++17)

-

optional sequence of any number of attributes

identifier

-

either a previously unused identifier, in which case this is original-namespace-definition or the name of a namespace, in which case this is extension-namespace-definition or a sequence of enclosing namespace specifiers separated by ::, ending with identifier, in which case this is a nested-namespace-definition (since C++17)

namespace-body

-

possibly empty sequence of declarations of any kind (including class and function definitions as well as nested namespaces)

命名空间定义只允许在命名空间范围,包括全局范围。

若要正式重新打开现有命名空间%28,则为扩展命名空间定义%29,命名空间定义中使用的标识符的查找必须解析为命名空间名称%28,而不是命名空间别名%29,该名称空间别名已声明为封闭名称空间的成员或封闭命名空间中内联命名空间的成员。

命名空间体定义命名空间范围,这会影响到名称查找...

由出现在命名空间-body%28中的声明引入的所有名称(包括嵌套命名空间定义%29)都成为命名空间标识符的成员,无论此名称空间定义%28是引入标识符%29的原始命名空间定义%28,还是“重新打开”已经定义的命名空间%29的扩展命名空间定义%28。

在命名空间主体中声明的命名空间成员可以使用显式限定在其外部定义或重新声明。

二次

代码语言:javascript
复制
namespace Q {
  namespace V { // V is a member of Q, and is fully defined within Q
// namespace Q::V { // C++17 alternative to the above two lines
    class C { void m(); }; // C is a member of V and is fully defined within V
                           // C::m is only declared
    void f(); // f is a member of V, but is only declared here
  }
  void V::f() // definition of V's member f outside of V
              // f's enclosing namespaces are still the global namespace, Q, and Q::V
  {
      extern void h(); // This declares ::Q::V::h
  }
  void V::C::m() // definition of V::C::m outside of the namespace (and the class body)
                 // enclosing namespaces are the global namespace, Q, and Q::V
  {
  }
}

二次

命名空间外的定义和重声明只允许在声明点之后,仅在名称空间范围内,并且仅在包含原始命名空间%28(包括全局命名空间%29)的命名空间中,而且它们必须使用自C++14%29以来的限定id语法%28。

二次

代码语言:javascript
复制
namespace Q {
  namespace V { // original-namespace-definition for V
    void f(); // declaration of Q::V::f
  }
  void V::f() {} // OK
  void V::g() {} // Error: g() is not yet a member of V
  namespace V { // extension-namespace-definition for V
    void g(); // declaration of Q::V::g
  }
}
namespace R { // not a enclosing namespace for Q
   void Q::V::g() {} // Error: cannot define Q::V::g inside R
}
void Q::V::g() {} // OK: global namespace encloses Q

二次

引入的名称朋友非本地类X中的声明成为X最内部封闭命名空间的成员,但它们对X不可见。查找%28除非在类定义之前或之后在命名空间范围内提供匹配声明,否则不提供不合格的%29或限定的%29。这样的名字可以通过ADL它同时考虑名称空间和类。

在决定名称是否会与先前声明的名称冲突时,此类朋友声明只考虑最内部的封闭命名空间。

二次

代码语言:javascript
复制
void h(int);
namespace A {
  class X {
    friend void f(X); // A::f is a friend
    class Y {
        friend void g(); // A::g is a friend
        friend void h(int); // A::h is a friend, no conflict with ::h
    };
  };
  // A::f, A::g and A::h are not visible at namespace scope
  // even though they are members of the namespace A
  X x;
  void g() {  // definition of A::g
     f(x); // A::X::f is found through ADL
  }
  void f(X) {}       // definition of A::f
  void h(int) {}     // definition of A::h
  // A::f, A::g and A::h are now visible at namespace scope
  // and they are also friends of A::X and A::X::Y
}

二次

Inline namespaces An inline namespace is a namespace that uses the optional keyword inline in its original-namespace-definition. Members of an inline namespace are treated as if they are members of the enclosing namespace in many situations (listed below). This property is transitive: if a namespace N contains an inline namespace M, which in turn contains an inline namespace O, then the members of O can be used as though they were members of M or N. A using-directive that names the inline namespace is implicitly inserted in the enclosing namespace (similar to the implicit using-directive for the unnamed namespace) In argument-dependent lookup, when a namespace is added to the set of associated namespaces, its inline namespaces are added as well, and if an inline namespace is added to the list of associated namespaces, its enclosing namespace is added as well. Each member of an inline namespace can be partially specialized, explicitly instantiated or explicitly specialized as if it were a member of the enclosing namespace. Qualified name lookup that examines the enclosing namespace will include the names from the inline namespaces even if the same name is present in the enclosing namespace. { // in C++14, std::literals and its member namespaces are inline using namespace std::string_literals; // makes visible operator""s // from std::literals::string_literals auto str = "abc"s; } { using namespace std::literals; // makes visible both // std::literals::string_literals::operator""s // and std::literals::chrono_literals::operator""s auto str = "abc"s; auto min = 60s; } { using std::operator""s; // makes both std::literals::string_literals::operator""s // and std::literals::chrono_literals::operator""s visible auto str = "abc"s; auto min = 60s; } Note: the rule about specializations allows library versioning: different implementations of a library template may be defined in different inline namespaces, while still allowing the user to extend the parent namespace with an explicit specialization of the primary template.

(since C++11)

  • 使用指令将内联命名空间隐式插入到封闭命名空间%28中,类似于未命名命名空间%29的隐式使用指令。
  • 在参数相关查找,当一个命名空间被添加到一组关联的命名空间中时,它的内联命名空间也会被添加,如果将内联名称空间添加到关联名称空间列表中,则还会添加其封闭的命名空间。
  • 内联名称空间的每个成员都可以被部分专门化、显式实例化或显式专门化,就好像它是封闭命名空间的成员一样。
  • 合资格名称查找这将检查封闭名称空间将包括内联名称空间中的名称,即使在包围名称空间中存在相同的名称。

{//在C++14中,std::文本及其成员命名空间使用命名空间std::string内联[医]文字;//使可见运算符“”s//from std::stanals::string[医]文字自动str=“abc”;}{使用命名空间std::stanals;//使/std::stanals::string都可见[医]文字::运算符“s/and std::文本::rono[医]文字::运算符“s autostr=”abc“s;automin=60;}{使用std::Operator”s;//使std::stanals::string同时使用[医]文字::运算符“s/and std::文本::rono[医]文字::操作符“s Visible autostr=”abc“s;automin=60;}

注意:关于专门化的规则允许库版本控制:库模板的不同实现可以在不同的内联命名空间中定义,同时仍然允许用户使用主模板的显式专门化扩展父命名空间。

%28自C++11%29

未命名命名空间

未命名-命名空间定义窗体的命名空间定义。

inline(optional) namespace attr(optional) { namespace-body }

?

?

attr(C++17)

-

optional sequence of any number of attributes

此定义被视为具有唯一名称的命名空间的定义,并且使用指令在指定此未命名命名空间的当前范围中。

二次

代码语言:javascript
复制
namespace {
    int i;  // defines ::(unique)::i
}
void f() {
    i++;  // increments ::(unique)::i
}
 
namespace A {
    namespace {
        int i; // A::(unique)::i
        int j; // A::(unique)::j
    }
    void g() { i++; } // A::unique::i++
}
 
using namespace A; // introduces all names from A into global namespace
void h() {
    i++;    // error: ::(unique)::i and ::A::(unique)::i are both in scope
    A::i++; // ok, increments ::A::(unique)::i
    j++;    // ok, increments ::A::(unique)::j
}

二次

Even though names in an unnamed namespace may be declared with external linkage, they are never accessible from other translation units because their namespace name is unique.

(until C++11)

Unnamed namespaces as well as all namespaces declared directly or indirectly within an unnamed namespace have internal linkage, which means that any name that is declared within an unnamed namespace has internal linkage.

(since C++11)

使用-声明

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

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 form typename(optional) nested-name-specifier unqualified-id. The last declarator may be an ellipsis, although that form is only meaningful in derived class definitions

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

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)

有关派生类定义中的用法,请参见使用声明...

通过使用声明引入命名空间范围的名称可以与任何其他名称一样使用,包括从其他作用域进行的限定查找:

二次

代码语言:javascript
复制
void f();
namespace A {
    void g();
}
namespace X {
    using ::f; // global f is now visible as ::X::f
    using A::g; // A::g is now visible as ::X::g
    using A::g, A::g; // (C++17) OK: double declaration allowed at namespace scope
}
void h()
{
    X::f(); // calls ::f
    X::g(); // calls A::g
}

二次

如果在使用-声明从名称空间中获取成员之后,扩展了名称空间并引入了相同名称的附加声明,则这些附加声明不会通过use-声明%28可见,而与use-指令%29相反。一个例外情况是,当使用声明命名类模板时:后面引入的部分专门化实际上是可见的,因为它们的查找继续通过主模板。

二次

代码语言:javascript
复制
namespace A {
    void f(int);
}
using A::f; // ::f is now a synonym for A::f(int)
 
namespace A { // namespace extension
   void f(char); // does not change what ::f means
}
void foo() {
    f('a'); // calls f(int), even though f(char) exists.
} 
void bar() {
   using A::f; // this f is a synonym for both A::f(int) and A::f(char)
   f('a'); // calls f(char)
}

二次

使用-声明不能命名模板id、命名空间或范围内的枚举数。使用声明中的每个声明器都会引入一个且只有一个名称,例如,将-声明用于枚举不引入任何枚举数。

对同名、隐藏和重载规则的常规声明的所有限制适用于使用-声明:

二次

代码语言:javascript
复制
namespace A {
    int x;
}
namespace B {
    int i;
    struct g { };
    struct x { };
    void f(int);
    void f(double);
    void g(char); // OK: function name g hides struct g
}
void func() {
    int i;
    using B::i; // error: i declared twice
 
    void f(char);
    using B::f; // OK: f(char), f(int), f(double) are overloads
    f(3.5); // calls B::f(double)
 
    using B::g;
    g('a');      // calls B::g(char)
    struct g g1; // declares g1 to have type struct B::g
 
    using B::x;
    using A::x;  // OK: hides struct B::x
    x = 99;      // assigns to A::x
    struct x x1; // declares x1 to have type struct B::x
}

二次

如果一个函数是由使用-声明引入的,则声明具有相同名称和参数列表的函数的格式为%28,除非声明是针对相同的函数%29。如果函数模板是通过使用-声明引入的,则声明具有相同名称的函数模板、参数类型列表、返回类型和模板参数列表是不正确的。两个使用-声明可以引入具有相同名称和参数列表的函数,但是如果尝试调用该函数,则程序的格式不正确。

二次

代码语言:javascript
复制
namespace B {
    void f(int);
    void f(double);
}
namespace C {
    void f(int);
    void f(double);
    void f(char);
}
void h() {
    using B::f; // introduces B::f(int), B::f(double)
    using C::f; // introduces C::f(int), C::f(double), and C::f(char)
    f('h');      // calls C::f(char)
    f(1);        // error: B::f(int) or C::f(int)?
    void f(int); // error: f(int) conflicts with C::f(int) and B::f(int)
}

二次

If an entity is declared, but not defined in some inner namespace, and then declared through using-declaration in the outer namespace, and then a definition appears in the outer namespace with the same unqualified name, that definition is a member of the outer namespace and conflicts with the using-declration: namespace X { namespace M { void g(); // declares, but doesn't define X::M::g() } using M::g; void g(); // Error: attempt to declare X::g which conflicts with X::M::g() } More generally, a declaration that appears in any namespace scope and introduces a name using an unqualified identifier always introduces a member into the namespace it's in and not to any other namespace. The exceptions are explicit instantiations and explicit specializations of a primary template that is defined in an inline namespace: because they do not introduce a new name, they may use unqualified-id in an enclosing namespace.

(since C++14)

使用-指令

使用指令是整块声明使用以下语法:

attr(optional) using namespace nested-name-specifier(optional) namespace-name ;

(1)

?

attr(C++11)

-

any number of attributes that apply to this using-directive

nested-name-specifier

-

a sequence of names and scope resolution operators ::, ending with a scope resolution operator. A single :: refers to the global namespace.

namespace-name

-

a name of a namespace. When looking up this name, lookup considers namespace declarations only

只允许在命名空间中使用-指令。范围在块范围内。从不合格的角度来看名称查找在任何名称的使用指令之后,直到它出现的范围结束,名称空间名称中的每个名称都是可见的,就好像它是在最近的包含使用指令和命名空间名称的封闭名称空间中声明的。

“使用-指令”不向其出现%28的声明区域添加任何名称,与“使用-声明”%29不同,因此不阻止声明相同的名称。

使用-指令是传递的,目的是不合格查找如果一个作用域包含一个命名空间名称的使用指令,而命名空间名称本身包含对某个命名空间名称-2的使用-指令,那么效果就好像第二个名称空间的使用指令出现在第一个名称空间中。这些传递命名空间发生的顺序不影响名称查找。

二次

代码语言:javascript
复制
namespace A {
    int i;
}
namespace B {
    int i;
    int j;
    namespace C {
        namespace D {
            using namespace A; // all names from A injected into global namespace
            int j;
            int k;
            int a = i; // i is B::i, because A::i is hidden by B::i
        }
        using namespace D; // names from D are injected into C
                           // names from A are injected into global namespace
        int k = 89; // OK to declare name identical to one introduced by a using
        int l = k;  // ambiguous: C::k or D::k
        int m = i;  // ok: B::i hides A::i
        int n = j;  // ok: D::j hides B::j
    }
}

二次

如果在使用use-指令指定某个命名空间后,扩展了命名空间,并且/或向其添加了使用-指令,那么这些额外的成员和附加的命名空间就可以通过use-指令%28可见,而不是使用-声明%29。

二次

代码语言:javascript
复制
namespace D {
   int d1;
   void f(char);
}
using namespace D; // introduces D::d1, D::f, D::d2, D::f,
                   //  E::e, and E::f into global namespace!
 
int d1; // OK: no conflict with D::d1 when declaring
namespace E {
    int e;
    void f(int);
}
namespace D { // namespace extension
    int d2;
    using namespace E; // transitive using-directive
    void f(int);
}
void f() {
    d1++; // error: ambiguous ::d1 or D::d1?
    ::d1++; // OK
    D::d1++; // OK
    d2++; // OK, d2 is D::d2
    e++; // OK: e is E::e due to transitive using
    f(1); // error: ambiguous: D::f(int) or E::f(int)?
    f('a'); // OK: the only f(char) is D::f(char)
}

二次

注记

使用指令using namespace std;在任何名称空间范围内,都会从名称空间中引入每个名称。std进入全局命名空间%28,因为全局命名空间是包含两者的最近的名称空间。std和任何用户声明的命名空间%29,这可能会导致不希望的名称冲突.。在头文件的文件范围内,这和其他使用指令通常被认为是错误的做法。

此示例演示如何使用命名空间创建已在std命名空间。

二次

代码语言:javascript
复制
#include <vector>
 
namespace vec {
 
    template< typename T >
    class vector {
        // ...
    };
 
} // of vec
 
int main()
{
    std::vector<int> v1; // Standard vector.
    vec::vector<int> v2; // User defined vector.
 
    v1 = v2; // Error: v1 and v2 are different object's type.
 
    {
        using namespace std;
        vector<int> v3; // Same as std::vector
        v1 = v3; // OK
    }
 
    {
        using vec::vector;
        vector<int> v4; // Same as vec::vector
        v2 = v4; // OK
    }
 
    return 0;
}

二次

缺陷报告

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

DR

Applied to

Behavior as published

Correct behavior

CWG 1838

C++14

unqualified definition in an outer namespace could define an entry declared, but not defined in another namespace and pulled in by a using.

unqualified defns always refers to its namespace

另见

namespace alias

creates an alias of an existing namespace

代码语言:txt
复制
 ? cppreference.com

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

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com