Team LiB
Previous Section Next Section

17.2. Namespaces

17.2. 命名空间

Every name defined in a given scope must be unique within that scope. This requirement can be difficult to satisfy for large, complex applications. Such applications tend to have many names defined in the global scope. Complex programs composed of independently developed libraries are even more likely to encounter name collisionsthe same name is used in our own code or (more often) in the code supplied to us by independent producers.

在一个给定作用域中定义的每个名字在该作用域中必须是唯一的,对庞大、复杂的应用程序而言,这个要求可能难以满足。这样的应用程序的全局作用域中一般有许多名字定义。由独立开发的库构成的复杂程序更有可能遇到名字冲突——同样的名字既可能在我们自己的代码中使用,也可能(更常见地)在独立供应商提供的代码中使用。

Libraries tend to define a large number of global namesprimarily names of templates, types and functions. When writing an application using libraries from many different vendors, it is almost inevitable that some of these names will clash. This name-clashing problem is known as the namespace pollution problem.

库倾向于定义许多全局名字——主要是模板名、类型名或函数名。在使用来自多个供应商的库编写应用程序的时候,这些名字中有一些几乎不可避免地会发生冲突,这种名字冲突问题称为命名空间污染问题。

Traditionally, programmers avoided namespace pollution by making names of global entities very long, often prefixing the names in their program with specific character sequences:

传统上,程序员通过将全局实体的名字设得很长来避免命名空间污染,经常用特定字符序列作为程序中名字的前缀:

     class cplusplus_primer_Query { ... };
     ifstream&
     cplusplus_primer_open_file(ifstream&, const string&);

This solution is far from ideal: It can be cumbersome for programmers to write and read programs that use such long names. Namespaces provide a much more controlled mechanism for preventing name collisions. Namespaces partition the global namespace, making it easier to use independently produced libraries. A namespace is a scope. By defining a library's names inside a namespace, library authors (and users) can avoid the limitations inherent in global names.

这个解决方案很不理想:程序员编写和阅读使用这种长名字的程序非常麻烦。命名空间为防止名字冲突提供了更加可控的机制,命名空间能够划分全局命名空间,这样使用独立开发的库就更加容易了。一个命名空间是一个作用域,通过在命名空间内部定义库中的名字,库的作者(以及用户)可以避免全局名字固有的限制。

17.2.1. Namespace Definitions

17.2.1. 命名空间的定义

A namespace definition begins with the keyword namespace followed by the namespace name.

命名空间定义以关键字 namespace 开始,后接命名空间的名字。

     namespace cplusplus_primer {
         class Sales_item { /* ... */};
         Sales_item operator+(const Sales_item&,
                              const Sales_item&);
         class Query {
         public:
             Query(const std::string&);
             std::ostream &display(std::ostream&) const;
             // ...
         };
         class Query_base { /* ... */};
     }

This code defines a namespace named cplusplus_primer with four members: two classes, an overloaded + operator, and a function.

这段代码定义了名为 cplusplus_primer 的命名空间,它有四个成员:两个类,一个重载的 + 操作符,一个函数。

As with other names, the name of a namespace must be unique within the scope in which the namespace is defined. Namespaces may be defined at global scope or inside another namespace. They may not be defined inside a function or a class.

像其他名字一样,命名空间的名字在定义该命名空间的作用域中必须是唯一的。命名空间可以在全局作用域或其他作用域内部定义,但不能在函数或类内部定义。

Following the namespace name is a block of declarations and definitions delimited by curly braces. Any declaration that can appear at global scope can be put into a namespace: classes, variables (with their initializations), functions (with their definitions), templates, and other namespaces.

命名空间名字后面接着由花括号括住的一块声明和定义,可以在命名空间中放入可以出现在全局作用域的任意声明:类、变量(以及它们的初始化)、函数(以及它们的定义)、模板以及其他命名空间。

A namespace scope does not end with a semicolon.

命名空间作用域不能以分号结束。



Each Namespace Is a Scope
每个命名空间是一个作用域

The entities defined in a namespace are called namespace members. Just as is the case for any scope, each name in a namespace must refer to a unique entity within that namespace. Because different namespaces introduce different scopes, different namespaces may have members with the same name.

定义在命名空间中的实体称为命名空间成员。像任意作用域的情况一样,命名空间中的每个名字必须引用该命名空间中的唯一实体。因为不同命名空间引入不同作用域,所以不同命名空间可以具有同名成员。

Names defined in a namespace may be accessed directly by other members of the namespace. Code outside the namespace must indicate the namespace in which the name is defined:

在命名空间中定义的名字可以被命名空间中的其他成员直接成员,命名空间外部的代码必须指出名字定义在哪个命名空间中:

     cplusplus_primer::Query q =
                     cplusplus_primer::Query("hello");
     q.display(cout);
     // ...

If another namespace (say, AddisonWesley) also provides a TextQuery class and we want to use that class instead of the one defined in cplusplus_primer, we can do so by modifying our code as follows:

如果另一命名空间(如 AddisonWesley)也提供 TextQuery 类,而且我们想要使用那个类代替 cplusplus_primer 中定义的 TextQuery,可以通过这样修改代码而实现:

     AddisonWesley::Query q = AddisonWesley::Query("hello");
     q.display(cout);
     // ...

Using Namespace Members from Outside the Namespace
从命名空间外部使用命名空间成员

Of course, always referring to a namespace member using the qualified name

当然,总是使用限定名

     namespace_name::member_name

can be cumbersome. Just as we've been doing for names defined in the std namespace, we can write a using declaration (Section 3.1, p. 78) to obtain direct access to names we know we'll use frequently:

引用命名空间成员可能非常麻烦。像对 std 中定义的命名空间所做的那样,可以编写 using 声明(第 3.1 节)来获得对我们知道将经常使用的名字的直接访问:

     using cplusplus_primer::Query;

After this using declaration, our program can use the name Query directly without the cplusplus_primer qualifier. We'll see other ways to simplify access in Section 17.2.4 (p. 720).

在这个 using 声明之后,程序可以无须 cplusplus_primer 限定符而直接使用名字 Query,在第 17.2.4 节将介绍简化访问的其他方法。

Namespaces Can Be Discontiguous
命名空间可以是不连续的

Unlike other scopes, a namespace can be defined in several parts. A namespace is made up of the sum of its separately defined parts; a namespace is cumulative. The separate parts of a namespace can be spread over multiple files. Namespace definitions in different text files are also cumulative. Of course, the usual restriction continues to apply that names are visible only in the files in which they are declared. So, if one part of the namespace requires a name defined in another file, that name must still be declared.

与其他作用域不同,命名空间可以在几个部分中定义。命名空间由它的分离定义部分的总和构成,命名空间是累积的。一个命名空间的分离部分可以分散在多个文件中,在不同文本文件中的命名空间定义也是累积的。当然,名字只在声明名字的文件中可见,这一常规限制继续应用,所以,如果命名空间的一个部分需要定义在另一文件中的名字,仍然必须声明该名字。

Writing a namespace definition

编写命名空间定义:

     namespace namespace_name {
     // declarations
     }

either defines a new namespace or adds to an existing one.

既可以定义新的命名空间,也可以添加到现在命名空间中。

If the name namespace_name does not refer to a previously defined namespace, then a new namespace with that name is created. Otherwise, this definition opens an existing namespace and adds these new declarations to that namespace.

如果名字 namespace_name 不是引用前面定义的命名空间,则用该名字创建新的命名空间,否则,这个定义打开一个已存在的命名空间,并将这些新声明加到那个命名空间。

Separation of Interface and Implementation
接口和实现的分离

The fact that namespace definitions can be discontiguous means that we can compose a namespace from separate interface and implementation files. Thus, a namespace can be organized in the same way that we manage our own class and function definitions:

命名空间定义可以不连续意味着,可以用分离的接口文件和实现文件构成命名空间,因此,可以用与管理自己的类和函数定义相同的方法来组织命名空间:

  1. Namespace members that define classes and declarations for the functions and objects that are part of the class interface can be put into header files. These headers can be included by files that use namespace members.

    定义类的命名空间成员,以及作为类接口的一部分的函数声明与对象声明,可以放在头文件中,使用命名空间成员的文件可以包含这些头文件。

  2. The definitions of namepsace members can be put in separate source files.

    命名空间成员的定义可以放在单独的源文件中。

Organizing our namespaces this way also satisfies the requirement that various entitiesnon-inline functions, static data members, variables, and so forthmay be defined only once in a program. This requirement applies equally to names defined in a namespace. By separating the interface and implementation, we can ensure that the functions and other names we need are defined only once, but the same declaration will be seen whenever the entity is used.

按这种方式组织命名空间,也满足了不同实体(非内联函数、静态数据成员、变量等)只能在一个程序中定义一次的要求,这个要求同样适用于命名空间中定义的名字。通过将接口和实现分离,可以保证函数和其他我们需要的名字只定义一次,但相同的声明可以在任何使用该实体的地方见到。

Namespaces that define multiple, unrelated types should use separate files to represent each type that the namespace defines.

定义多个不相关类型的命名空间应该使用分离的文件,表示该命名空间定义的每个类型。



Defining the Primer Namespace
定义本书的命名空间

Using this strategy for separating interface and implementation, we might define the cplusplus_primer library in several separate files. The declarations for Sales_item and its related functions that we built in Part I (p. 31) would be placed in Sales_item.h, those for the Query classes of Chapter 15 (p. 557) in Query.h, and so on. The corresponding implementation files would be in files such as Sales_item.cc and Query.cc:

使用将接口和实现分离的策略,可以将 cplusplus_primer 库定义在几个分离的文件中。本书第一部分建立的 Sales_item 的声明及其相关函数可以放在 Sales_item.h 中,第十五章Query 类的定义以及相关函数放在 Query.h 中,以此类推。对应的实现文件可以是 Sales_item.ccQuery.cc:

     // ---- Sales_item.h ----
     namespace cplusplus_primer {
         class Sales_item { /* ... */};
         Sales_item operator+(const Sales_item&,
                              const Sales_item&);
         // declarations for remaining functions in the Sales_item interface
     }
     // ---- Query.h ----
     namespace cplusplus_primer {
         class Query {
         public:
             Query(const std::string&);
             std::ostream &display(std::ostream&) const;
             // ...
         };
         class Query_base { /* ... */};
     }
     // ---- Sales_item.cc ----
     #include "Sales_item.h"
     namespace cplusplus_primer {
     // definitions for Sales_item members and overloaded operators
     }
     // ---- Query.cc ----
     #include "Query.h"
     namespace cplusplus_primer {
         // definitions for Query members and related functions
     }

This program organization gives both the developers and users of our library the needed modularity. Each class is still organized into its own interface and implementation files. A user of one class need not compile names related to the others. We can hide the implementations from our users, while allowing the files Sales_item.cc and user.cc to be compiled and linked into one program without causing any compile-time or link-time error. Developers of the library can work independently on the implementation of each type.

这种程序组织给予开发者和库用户必要的模块性。每个类仍组织在自己的接口和实现文件中,一个类的用户不必编译与其他类相关的名字。如果允许 Sales_item.ccuser.cc 文件编译和链接到一个程序而不会导致编译时错误和运行时错误,就可以对用户隐藏实现。库的开发者可以独立工作于每个类型的实现。

A program using our library would include whichever headers it needed. The names in those headers are defined inside the cplusplus_primer namespace:

使用我们的库的程序可以包含需要的头文件,那些头文件中的名字定义在命名空间 cplusplus_primer 内部:

     // ---- user.cc ----
     // defines the cplusplus_primer::Sales_item class
     #include "Sales_item.h"
     int main()
     {
         // ...
         cplusplus_primer::Sales_item trans1, trans2;
         // ...
         return 0;
     }

Defining Namespace Members
定义命名空间成员

Functions defined inside a namespace may use the short form for names defined in the same namespace:

在命名空间内部定义的函数可以使用同一命名空间中定义的名字的简写形式:

     namespace cplusplus_primer {
     // members defined inside the namespace may use unqualified names
     std::istream&
     operator>>(std::istream& in, Sales_item& s)
     {
         // ...
     }

It is also possible to define a namespace member outside its namespace definition. We do so in ways that are similar to defining class members outside a class: The namespace declaration of the name must be in scope, and the definition must specify the namespace to which the name belongs:

也可以在命名空间定义的外部定义命名空间成员,用类似于在类外部定义类成员的方式:名字的命名空间声明必须在作用域中,并且定义必须指定该名字所属的命名空间:

     // namespace members defined outside the namespace must use qualified names
     cplusplus_primer::Sales_item
     cplusplus_primer::operator+(const Sales_item& lhs,
                                 const Sales_item& rhs)
     {
         Sales_item ret(lhs);
         // ...
     }

This definition should look similar to class member functions defined outside a class. The return type and function name are qualified by the namespace name. Once the fully qualified function name is seen, we are in the scope of the namespace. Thus, references to namespace members in the parameter list and the function body can use unqualified names to reference Sales_item.

这个定义看起来类似于定义在类外部的类成员函数,返回类型和函数名由命名空间名字限定。一旦看到完全限定的函数名,就处于命名空间的作用域中。因此,形参表和函数体中的命名空间成员引用可以使用非限定名引用 Sales_item

Members May Not Be Defined in Unrelated Namespaces
不能在不相关的命名空间中定义成员

Although a namespace member can be defined outside its namespace definition, there are restrictions on where this definition can appear. Only namespaces enclosing the member declaration can contain its definition. For example, operator+ could be defined in either the cplusplus_primer namespace or at global scope. It may not be defined in an unrelated namespace.

虽然可以在命名空间定义的外部定义命名空间成员,对这个定义可以出现的地方仍有些限制,只有包围成员声明的命名空间可以包含成员的定义。例如,operator+ 既可以定义在命名空间 cplusplus_primer 中,也可以定义在全局作用域中,但它不能定义在不相关的命名空间中。

The Global Namespace
全局命名空间

Names defined at global scopenames declared outside any class, function, or namespaceare defined inside the global namespace. The global namespace is implicitly declared and exists in every program. Each file that defines entities at global scope adds those names to the global namespace.

定义在全局作用域的名字(在任意类、函数或命名空间外部声明的名字)是定义在全局命名空间中的。全局命名空间是隐式声明的,存在于每个程序中。在全局作用域定义实体的每个文件将那些名字加到全局命名空间。


The scope operator can be used to refer to members of the global namespace. Because the global namespace is implicit, it does not have a name; the notation

可以用作用域操作符引用全局命名空间的成员。因为全局命名空间是隐含的,它没有名字,所以记号

     ::member_name

refers to a member of the global namespace.

引用全局命名空间的成员。

Exercises Section 17.2.1

Exercise 17.13:

Define the bookstore exception classes described in Section 17.1.7 (p. 697) as members of namespace named Bookstore.

定义第 17.7 节描述的书店异常类,作为名为 Bookstore 的命名空间的成员。

Exercise 17.14:

Define Sales_item and its operators inside the Bookstore namespace. Define the addition operator to throw an exception.

在命名空间 Bookstore 内部定义 Sales_item 及其操作符。定义加操作符抛出一个异常。

Exercise 17.15:

Write a program that uses the Sales_item addition operator and handles any exceptions. Make this program a member of another namespace named MyApp. This program should use the exception classes defined in the Bookstore namespace by the previous exercise.

编写一个程序,使用 Sales_item 加操作符并处理任何异常。使这个程序成为名为 MyApp 的另一命名空间的成员。这个程序应使用上题中在命名空间 Bookstore 中定义的异常类。


17.2.2. Nested Namespaces

17.2.2. 嵌套命名空间

A nested namespace is a nested scopeits scope is nested within the namespace that contains it. Names in nested namespaces follow the normal rules: Names declared in an enclosing namespace are hidden by declarations of the same name in a nested namespace. Names defined inside a nested namespace are local to that namespace. Code in the outer parts of the enclosing namespace may refer to a name in a nested namespace only through its qualified name.

一个嵌套命名空间即是一个嵌套作用域——其作用域嵌套在包含它的命名空间内部。嵌套命名空间中的名字遵循常规规则:外围命名空间中声明的名字被嵌套命名空间中同一名字的声明所屏蔽。嵌套命名空间内部定义的名字局部于该命名空间。外围命名空间之外的代码只能通过限定名引用嵌套命名空间中的名字。

Nested namespaces can improve the organization of code in a library:

嵌套命名空间可以改进库中代码的组织:

     namespace cplusplus_primer {
         // first nested namespace:
         // defines the Query portion of the library
         namespace QueryLib {
             class Query { /* ... */ };
             Query operator&(const Query&, const Query&);
             // ...
         }
         // second nested namespace:
         // defines the Sales_item portion of the library
         namespace Bookstore {
             class Item_base { /* ... */ };
             class Bulk_item : public Item_base { /* ... */ };
             // ...
         }
     }

The cplusplus_primer namespace now contains two nested namespaces: the namespaces named QueryLib and Bookstore.

命名空间 cplusplus_primer 现在包含两个嵌套命名空间:名为 QueryLib 的命名空间和名为 Bookstore 的命名空间。

Nested namespaces are useful when a library provider needs to prevent names in each part of a library from colliding with names in other parts of the library.

当库提供者需要防止库中每个部分的名字与库中其他部分的名字冲突的时候,嵌套命名空间是很有用的。

The name of a member in a nested namespace is formed from the names of the enclosing namespace(s) and the name of the nested namespace. For example, the name of the class declared in the nested namespace QueryLib is

嵌套命名空间中成员的名字由外围命名空间的名字和嵌套命名空间的名字构成。例如,嵌套命名空间 QueryLib 中声明的类的名字是

     cplusplus_primer::QueryLib::Query

Exercises Section 17.2.2

Exercise 17.16:

Organize the programs you have written to answer the questions in each chapter into its own namespace. That is, namespace chapterrefinheritance would contain code for the Query programs and chapterrefalgs would contain the TextQuery code. Using this structure, compile the Query code examples.

将为回答每章中的问题而编写的程序组织到每一章自己的命名空间中,也就是说,命名空间 chapterrefinheritance 将包含 Query 程序的代码,而 chapterrefalgs 将包含 TextQuery 代码。使用这个结构,编译 Query 代码示例。

Exercise 17.17:

Over the course of this primer, we defined two different classes named Sales_item: the initial simple class defined and used in Part I, and the handle class defined in Section 15.8.1 that interfaced to the Item_base inheritance hierarchy. Define two namespaces nested inside the cplusplus_primer namespace that could be used to distinguish these two class definitions.

在本书中,我们定义了两个名为 Sales_item 的不同类:在第一部分定义和使用的初始简单类,以及在第 15.8.1 节定义的与 Item_base 继承层次接口的句柄类。在命名空间 cplusplus_primer 内部定义两个嵌套命名空间,用于区别这两个类定义。


17.2.3. Unnamed Namespaces

17.2.3. 未命名的命名空间

A namespace may be unnamed. An unnamed namespace is a namespace that is defined without a name. An unnamed namespace begins with the keyword namespace. Following the namespace keyword is a block of declarations delimited by curly braces.

命名空间可以是未命名的,未命名的命名空间在定义时没有给定名字。未命名的命名空间以关键字 namespace 开头,接在关键字 namespace 后面的是由花括号定界的声明块。

Unnamed namespaces are not like other namespaces; the definition of an unnamed namespace is local to a particular file and never spans multiple text files.

未命名的命名空间与其他命名空间不同,未命名的命名空间的定义局部于特定文件,从不跨越多个文本文件。



An unnamed namespace may be discontiguous within a given file but does not span files. Each file has its own unnamed namespace.

未命名的命名空间可以在给定文件中不连续,但不能跨越文件,每个文件有自己的未命名的命名空间。

Unnamed namespaces are used to declare entities that are local to a file. Variables defined in an unnamed namespace are created when the program is started and exist until the program ends.

未命名的命名空间用于声明局部于文件的实体。在未命名的命名空间中定义的变量在程序开始时创建,在程序结束之前一直存在。

Names defined in an unnamed namespace are used directly; after all, there is no namespace name with which to qualify them. It is not possible to use the scope operator to refer to members of unnamed namespaces.

未命名的命名空间中定义的名字可直接使用,毕竟,没有命名空间名字来限定它们。不能使用作用域操作符来引用未命名的命名空间的成员。

Names defined in an unnamed namespace are visible only to the file containing the namespace. If another file contains an unnamed namespace, the namespaces are unrelated. Both unnamed namespaces could define the same name, and the definitions would refer to different entities.

未命名的命名空间中定义的名字只在包含该命名空间的文件中可见。如果另一文件包含一个未命名的命名空间,两个命名空间不相关。两个命名空间可以定义相同的名字,而这些定义将引用不同的实体。

Names defined in an unnamed namespace are found in the same scope as the scope at which the namespace is defined. If an unnamed namespace is defined at the outermost scope in the file, then names in the unnamed namespace must differ from names defined at global scope:

未命名空间中定义的名字可以在定义该命名空间所在的作用域中找到。如果在文件的最外层作用域中定义未命名的命名空间,那么,未命名的空间中的名字必须与全局作用域中定义的名字不同:

     int i;   // global declaration for i
     namespace {
         int i;
     }
     // error: ambiguous defined globally and in an unnested, unnamed namespace
     i = 10;

An unnamed namespace, like any other namespace, may be nested inside another namespace. If the unnamed namespace is nested, then names in it are accessed in the normal way, using the enclosing namespace name(s):

像任意其他命名空间一样,未命名的命名空间也可以嵌套在另一命名空间内部。如果未命名的命名空间是嵌套的,其中的名字按常规方法使用外围命名空间名字访问:

     namespace local {
        namespace {
            int i;
        }
     }
        // ok: i defined in a nested unnamed namespace is distinct from global i
        local::i = 42;

If a header defines an unnamed namespace then the names in that namespace will define different local entities in each file that includes the header.

如果头文件定义了未命名的命名空间,那么,在每个包含该头文件的文件中,该命名空间中的名字将定义不同的局部实体。



In all other ways, the members of an unnamed namespace are normal program entities.

在所有其他方式中,未命名的命名空间的成员都是普通程序实体。

Unnamed Namespaces Replace File Statics

未命名的命名空间取代文件中的静态声明

Prior to the introduction of namespaces in standard C++, programs had to declare names as static to make them local to a file. The use of file statics is inherited from C. In C, a global entity declared static is invisible outside the file in which it is declared.

在标准 C++ 中引入命名空间之前,程序必须将名字声明为 static,使它们局部于一个文件。文件中静态声明的使用从 C 语言继承而来,在 C 语言中,声明为 static 的局部实体在声明它的文件之外不可见。

The use of file static declarations is deprecated by the C++ standard. A deprecated feature is one that may not be supported in future releases. File statics should be avoided and unnamed namespaces used instead.

C++ 不赞成文件静态声明。不造成的特征是在未来版本中可能不支持的特征。应该避免文件静态而使用未命名空间代替。




Exercises Section 17.2.3

Exercise 17.18:

Why would you define your own namespace in your programs? When might you use an unnamed namespace?

为什么在程序中可以定义自己的命名空间?何时可以使用未命名空间?

Exercise 17.19:

Suppose we have the following declaration of the operator* that is a member of the nested namespace cplusplus_primer::MatrixLib:

假定有下面的 operator* 的声明,operator* 是嵌套命名空间 cplusplus_primer::MatrixLib: 的成员:

     namespace cplusplus_primer {
         namespace MatrixLib {
             class matrix { /* ... */ };
             matrix operator*
                    (const matrix &, const matrix &);
             // ...
         }
     }

How would you define this operator in global scope? Provide only the prototype for the operator's definition.

怎样在全局作用域中定义这个操作符?只需给出操作符定义的原型。


17.2.4. Using Namespace Members

17.2.4. 命名空间成员的使用


Referring to namespace members as namespace_name::member_name is admittedly cumbersome, especially if the namespace name is long. Fortunately, there are ways to make it easier to use namespace members. Our programs have used one of these ways, using declarations (Section 3.1, p. 78). The others, namespace aliases and using directives, will be described in this section.

像命名空间名 namespace_name::member_name 成员名这样引用命名空间的成员无可否认是很麻烦,特别是,命名空间名字很长的时候。幸好,有办法让使用命名空间成员比较容易。我们的程序已经使用了其中的一种方法,就是 using第 3.1 节),本节将介绍其他方法:命名空间别名和 using 指示。

Header files should not contain using directives or using declarations except inside functions or other scopes. A header that includes a using directive or declaration at its top level scope has the effect of injecting that name into the file that includes the header. Headers should define only the names that are part of its interface, not names used in its own implementation.

除了在函数或其他作用域内部,头文件不应该包含 using 指示或 using 声明。在其顶级作用域包含 using 指示或 using 声明的头文件,具有将该名字注入包含该头文件的文件中的效果。头文件应该只定义作为其接口的一部分的名字,不要定义在其实现中使用的名字。



using Declarations, a Recap
using 声明,扼要重述

The programs in this book that use names from the standard library generally assume that an appropriate using declaration has been made:

本书中使用标准库中名字的程序一般假设进行了适当的 using 声明

     map<string, vector< pair<size_t, size_t> > > word_map;

assumes that the following using declarations have been made:

假定进行了下面的声明:

     using std::map;
     using std::pair;
     using std::size_t;
     using std::string;
     using std::vector;

A using declaration introduces only one namespace member at a time. It allows us to be very specific regarding which names are used in our programs.

一个 using 声明一次只引入一个命名空间成员,它使得无论程序中使用哪些名字,都能够非常明确。

Scope of a using Declaration
using 声明的作用域

Names introduced in a using declaration obey normal scope rules. The name is visible from the point of the using declaration to the end of the scope in which the declaration is found. Entities with the same name defined in an outer scope are hidden.

using 声明中引入的名字遵循常规作用域规则。从 using 声明点开始,直到包含 using 声明的作用域的末尾,名字都是可见的。外部作用域中定义的同名实体被屏蔽。

The shorthand name may be used only within the scope in which it is declared and in scopes nested within that scope. Once the scope ends, the fully qualified name must be used.

简写名字只能在声明它的作用域及其嵌套作用域中使用,一旦该作用域结束了,就必须使用完全限定名。

A using declaration can appear in global, local, or namespace scope. A using declaration in class scope is limited to names defined in a base class of the class being defined.

using 声明可以出现在全局作用域、局部作用域或者命名空间作用域中。类作用域中的 using 声明局限于被定义类的基类中定义的名字。

Namespace Aliases
命名空间别名

A namespace alias can be used to associate a shorter synonym with a namespace name. For example, a long namespace name such as

可用命名空间别名将较短的同义词与命名空间名字相关联。例如,像

     namespace cplusplus_primer { /* ... */ };

can be associated with a shorter synonym as follows:

这样的长命名空间名字,可以像下面这样与较短的同义词相关联:

     namespace primer = cplusplus_primer;

A namespace alias declaration begins with the keyword namespace, followed by the (shorter) name of the namespace alias, followed by the = sign, followed by the original namespace name and a semicolon. It is an error if the original namespace name has not already been defined as a namespace.

命名空间别名声明以关键字 namespace 开头,接(较短的)命名空间别名名字,再接 =,再接原来的命名空间名字和分号。如果原来的命名空间名字是未定义的,就会出错。

A namespace alias can also refer to a nested namespace. Rather than writing

命名空间别名也可以引用嵌套的命名空间。除了编写

     cplusplus_primer::QueryLib::Query tq;

we could define and use an alias for cplusplus_primer::QueryLib:

之外,我们可以定义和使用 cplusplus_primer::QueryLib: 的别名:

     namespace Qlib = cplusplus_primer::QueryLib;
     Qlib::Query tq;

A namespace can have many synonyms, or aliases. All the aliases and the original namespace name can be used interchangeably.

一个命名空间可以有许多别名,所有别名以及原来的命名空间名字都可以互换使用。


using Directives
using 指示

Like a using declaration, a using directive allows us to use the shorthand form of a namespace name. Unlike a using declaration, we retain no control over which names are made visiblethey all are.

using 声明一样,using 指示使我们能够使用命名空间名字的简写形式。与 using 声明不同,using 指示无法控制使得哪些名字可见——它们都是可见的。

The Form of a using Directive
using 指示的形式

A using directive begins with the keyword using, followed by the keyword namespace, followed by a namespace name. It is an error if the name is not a previously defined namespace name.

using 指示以关键字 using 开头,后接关键字 namespace,再接命名空间名字。如果该名字不是已经定义的命名空间名字,就会出错。

A using directive makes all the names from a specific namespace visible without qualification. The short form names can be used from the point of the using directive to the end of the scope in which the using directive appears.

using 指示使得特定命名空间所有名字可见,没有限制。短格式名字可从 using 指示点开始使用,直到出现 using 指示的作用域的末尾。

A using directive may appear in namespace, function, or block scope. It may not appear in a class scope.

using 指示使得特定命名空间的所有名字可见,没有限制。短格式名字可从 using 指示点开始使用,直到出现 using 指示的作用域的末尾。

It can be tempting to write programs with using directives, but doing so reintroduces all the problems inherent in name collisions when using multiple libraries.

可以尝试用 using 指示编写程序,但在使用多个库的时候,这样做会重新引入名字冲突的所有问题。



using Directives and Scope
using 指示与作用域

The scope of names introduced by a using directive is more complicated than those for using declarations. A using declaration puts the name directly in the same scope in which the using declaration itself appears. It is as if the using declaration is a local alias for the namespace member. Because the declaration is localized, the chance of collisions is minimized.

using 指示引入的名字的作用域比 using 声明的更复杂。using 声明将名字直接放入出现 using 声明的作用域,好像 using 声明是命名空间成员的局部别名一样。因为这种声明是局部化的,冲突的机会最小。

A using directive does not declare local aliases for the namespace member names. Rather, it has the effect of lifting the namespace members into the nearest scope that contains both the namespace itself and the using directive.

using 指示不声明命名空间成员名字的别名,相反,它具有将命名空间成员提升到包含命名空间本身和 using 指示的最近作用域的效果。



In the simplest case, assume we have a namespace A and a function f, both defined at global scope. If f has a using directive for A, then in f it will be as if the names in A appeared in the global scope prior to the definition of f:

在最简单的情况下,假定有命名空间 A 和函数 f,二者都在全局作用域中定义。如果 f 有关于 Ausing 指示,那么,在 f 中,将好像 A 中的名字出现在全局作用域中 f 的定义之前一样:

    // namespace A and function f are defined at global scope
    namespace A {
        int i, j;
    }
    void f()
    {
        using namespace A;      // injects names from A into the global scope
        cout << i * j << endl; // uses i and j from namespace A
        //...
    }

One place where using directives are useful is in the implementation files for the namespace itself.

using 指示有用的一种情况是,用在命名空间本身的实现文件中。



using Directives Example
using 指示例子

Let's look at an example:

看一个例子:

    namespace blip {
        int bi = 16, bj = 15, bk = 23;
        // other declarations
    }
    int bj = 0; // ok: bj inside blip is hidden inside a namespace
    void manip()
    {

         // using directive - names in blip "added" to global scope
         using namespace blip;
                         // clash between ::bj and blip::bj
                         // detected only if bj is used
         ++bi;           // sets blip::bi to 17
         ++bj;           // error: ambiguous
                         // global bj or blip::bj?
         ++::bj;         // ok: sets global bj to 1
         ++blip::bj;     // ok: sets blip::bj to 16
         int bk = 97;    // local bk hides blip::bk
         ++bk;           // sets local bk to 98
    }

The using directive in manip makes all the names in blip directly accessible to manip: The function can refer to the names of these members, using their short form.

manip 中的 using 提示使 manip 能够直接访问 blip 中的所有名字:使用它们的简化形式,该函数可以引用这些成员的名字。

The members of blip appear as if they were defined in the scope in which both blip and manip are defined. Given that blip is defined at global scope, then the members of blip appear as if they were declared in global scope. Because the names are in different scopes, local declarations within manip may hide some of the namespace member names. The local variable bk hides the namespace member blip::bk. Referring to bk within manip is not ambiguous; it refers to the local variable bk.

blip 的成员看来好像是在定义 blipmanip 的作用域中定义的一样。如果在全局作用域中定义 blip,则 blip 的成员看来好像是声明在全局作用域的一样。因为名字在不同的作用域中,manip 内部的局部声明可以屏蔽命名空间的某些成员名字,局部变量 bk 屏蔽命名空间名字 blip::bk,在 manip 内部对 bk 的引用没有二义性,它引用局部变量 bk

It is possible for names in the namespace to conflict with other names defined in the enclosing scope. For example, the blip member bj appears to manip as if it were declared at global scope. However, there is another object named bj in global scope. Such conflicts are permitted; but to use the name, we must explicitly indicate which version is wanted. Therefore, the use of bj within manip is ambiguous: The name refers both to the global variable and to the member of namespace blip.

命名空间中的名字可能会与外围作用域中定义的其他名字冲突。例如,对 manip 而言,blip 成员 bj 看来好像声明在全局作用域中,但是,全局作用域存在另一名为 bj 的对象。这种冲突是允许的,但为了使用该名字,必须显式指出想要的是哪个版本,因此,在 manip 内部的 bj 使用是有二义性的:该名字既可引用全局变量又可引用命名空间 blip 的成员。

To use a name such as bj, we must use the scope operator to indicate which name is wanted. We would write ::bj to obtain the variable defined in global scope. To use the bj defined in blip, we must use its qualified name, blip::bj.

为了使用像 bj 这样的名字,必须使用作用域操作符指出想要的是哪个名字。可以编写 ::bj 来获得在全局作用域中定义的变量,要使用 blip 中定义的 bj,必须使用它的限定名字 blip::bj

Exercises Section 17.2.4

Exercise 17.20:

Explain the differences between using declarations and using directives.

解释 using 声明和 using 指示之间的区别。

Exercise 17.21:

Consider the following code sample:

考虑下面代码样本:

    namespace Exercise {
        int ivar = 0;
        double dvar = 0;
        const int limit = 1000;
    }
    int ivar = 0;
    // position 1
    void manip() {
         // position 2
         double dvar = 3.1416;
         int iobj = limit + 1;
         ++ivar;
         ++::ivar;
    }

What are the effects of the declarations and expressions in this code sample if using declarations for all the members of namespace Exercise are located at the location labeled position 1? At position 2 instead? Now answer the same question but replace the using declarations with a using directive for namespace Exercise.

如果命名空间 Exercise 的所有成员的 using 声明放在标为 position 1 的地方,这个代码样本中的声明和表达式的效果是什么?如果放在 position 2 位置呢?用命名空间 Exerciseusing 指示代替 using 声明,回答同一问题。


Caution: Avoid Using Directives

警告:避免 Using 指示

using directives, which inject all the names from a namespace, are deceptively simple to use: With only a single statement, all the member names of a namespace are suddenly visible. Although this approach may seem simple, it can introduce its own problems. If an application uses many libraries, and if the names within these libraries are made visible with using directives, then we are back to square one, and the global namespace pollution problem reappears.

using 指示注入来自一个命名空间的所有名字,它的使用是靠不住的:只用一个语句,命名空间的所有成员名就突然可见了。虽然这个方法看似简单,但也有它自身的问题。如果应用程序使用许多库,并且用 using 指示使得这些库中的名字可见,那么,全局命名空间污染问题就重新出现。

Moreover, it is possible that a working program will fail to compile when a new version of the library is introduced. This problem can arise if a new version introduces a name that conflicts with a name that the application is using.

而且,当引入库的新版本的时候,正在工作的程序可能会编译失败。如果新版本引入一个与应用程序正在使用的名字冲突的名字,就会引发这个问题。

Another problem is that ambiguity errors caused by using directives are detected only at the point of use. This late detection means that conflicts can arise long after introducing a particular library. If the program begins using a new part of the library, previously undetected collisions may arise.

另一个问题是,由 using 指示引起的二义性错误只能在使用处检测,这个后来的检测意味着,可能在特定库引入很久之后才引发冲突,如果程序开始使用该库的新部分,就可能引发先前未检测到的冲突。

Rather than relying on a using directive, it is better to use a using declaration for each namespace name used in the program. Doing so reduces the number of names injected into the namespace. Ambiguity errors caused by using declarations are detected at the point of declaration, not use, and so are easier to find and fix.

相对于依赖于 using 指示,对程序中使用的每个命名空间名字使用 using 声明更好,这样做减少注入到命名空间中的名字数目,由 using 声明引起的二义性错误在声明点而不是使用点检测,因此更容易发现和修正。


17.2.5. Classes, Namespaces, and Scope

17.2.5. 类、命名空间和作用域

As we've noted, namespaces are scopes. As in any other scope, names are visible from the point of their declaration. Names remain visible through any nested scopes until the end of the block in which they were introduced.

正如我们已经注意到的,命名空间是作用域。像在任意其他作用域中一样,名字从声明点开始可见。名字的可见性穿过任意嵌套作用域,直到引入名字的块的末尾。

Name lookup for names used inside a namespace follows the normal C++ lookup rules: When looking for a name, we look outward through the enclosing scopes. An enclosing scope for a name used inside a namespace might be one or more nested namespaces ending finally with the all-encompassing global namespace. Only names that have been declared before the point of use that are in blocks that are still open are considered:

对命名空间内部使用的名字的查找遵循常规 C++ 查找规则:当查找名字的时候,通过外围作用域外查找。对命名空间内部使用的名字而言,外围作用域可能是一个或多个嵌套的命名空间,最终以全包围的全局命名空间结束。只考虑已经在使用点之前声明的名字,而该使用仍在开放的块中:

    namespace A {
        int i;
        namespace B {
            int i;        // hides A::i within B
            int j;
            int f1()
            {
                int j;    // j is local to f1 and hides A::B::j
                return i; // returns B::i
            }
        } // namespace B is closed and names in it are no longer visible
        int f2() {
           return j;     // error: j is not defined
        }
        int j = i;      // initialized from A::i
    }

Names used in a class member definition are resolved in much the same way, with one important difference: If the name is not local to the member function, we first try to resolve the name to a class member before looking in the outer scopes.

用非常相似的方式确定类成员定义中使用的名字,只有一个重要区别:如果名字不是局部于成员函数的,就试着在查找更外层作用域之前在类成员中确定名字。

As we saw in Section 12.3 (p. 444), members defined inside a class may use names that appear textually after the definition. For example, a constructor defined inside the class body may initialize the data members even if the declaration of those members appears after the constructor definition. When a name is used in a class scope, we look first in the member itself, then in the class, including any base classes. Only after exhausting the class(es) do we examine the enclosing scopes. When a class is wrapped in a namespace, the same lookup happens: Look first in the member, then the class (including base classes), then look in the enclosing scopes, one or more of which might be a namespace:

正如第 12.3 节所介绍的,类内部所定义的成员可以使用出现在定义文本之后的名字。例如,即使数据成员的定义出现在构造函数定义之后,类定义体内部定义的构造函数也可以初始化那些数据成员。当在类作用域中使用名字的时候,首先在成员本身中查找,然后在类中查找,包括任意基类,只有在查找完类之后,才检查外围作用域。当类包在命名空间中的时候,发生相同的查找:首先在成员中找,然后在类(包括基类)中找,再在外围作用域中找,外围作用域中的一个或多个可以是命名空间:

    namespace A {
        int i;
        int k;
        class C1 {
        public:
            C1(): i(0), j(0) { }   // ok: initializes C1::i and C1::j
            int f1()
            {
                 return k;        // returns A::k
            }
            int f2()
            {
                return h;        // error: h is not defined
            }
            int f3();
        private:
           int i;                // hides A::i within C1
           int j;
        };
        int h = i;               // initialized from A::i
     }
     // member f3 is defined outside class C1 and outside namespace A
     int A::C1::f3()
     {
         return h;               // ok: returns A::h
     }

With the exception of member definitions, scopes are always searched upward: A name must be declared before it can be used. Hence, the return in f2 will not compile. It attempts to reference the name h from namespace A, but h has not yet been defined. Had that name been defined in A before the definition of C1, the use of h would be legal. Similarly, the use of h inside f3 is okay, because f3 is defined after A::h has been defined.

除了成员定义例外,总是向上查找作用域:名字在使用之前必须声明。因此,f2 中的 return 语句将不能编译,它试图引用命名空间 A 中的名字 h,但 h 还没有定义。如果使 A 中的名字在 C1 的定义之前定义,h 的使用就是合法的。类似地,f3 内部对 h 的使用是正确的,因为 f3 定义在已经定义了 A::h 之后。

The order in which scopes are examined to find a name can be inferred from the qualified name of a function. The qualified name indicates, in reverse order, the scopes that are searched.

可以从函数的限定名推断出查找名字时所检查作用域的次序,限定名以相反次序指出被查找的作用域。



The qualifiers A::C1::f3 indicate the reverse order in which the class scopes and namespace scopes are to be searched. The first scope searched is that of the function f3. Then the class scope of its enclosing class C1 is searched. The scope of the namespace A is searched last before the scope containing the definition of f3 is examined.

限定符 A::C1::f3 指出了查找类作用域和命名空间作用域的相反次序,首先查找函数 f3 的作用域,然后查找外围类 C1 的作用域。在查找包含 f3 定义的作用之前,最后查找命名空间 A 的作用域。

Argument-Dependent Lookup and Class Type Parameters
实参相关的查找与类类型形参

Consider the following simple program:

考虑下面的简单程序:

    std::string s;
    // ok: calls std::getline(std::istream&, const std::string&)
    getline(std::cin, s);

The program uses the std::string type, yet it refers without qualification to the getline function. Why can we use this function without a specific std:: qualifier or a using declaration?

这段程序使用了 std::string 类型,但它不加限制地引用了 getline 函数。为什么可以无须特定 std:: 限定符或 using 声明而使用该函数?

It turns out that there is an important exception to the rule that namespace names are hidden.

它给出了屏蔽命名空间名字规则的一个重要例外。

Functions, including overloaded operators, that take parameters of a class type (or pointer or reference to a class type), and that are defined in the same namespace as the class itself, are visible when an object of (or reference or pointer to) the class type is used as an argument.

接受类类型形参(或类类型指针及引用形参)的函数(包括重载操作符),以及与类本身定义在同一命名空间中的函数(包括重载操作符),在用类类型对象(或类类型的引用及指针)作为实参的时候是可见的。



When the compiler sees the use of the getline function

当编译器看到 getline 函数的使用


    getline(std::cin, s);

it looks for a matching function in the current scope, the scopes enclosing the call to getline, and in the namespace(s) in which the type of cin and the string type are defined. Hence, it looks in the namespace std and finds the getline function defined by the string type.

的时候,它在当前作用域,包含调用的作用域以及定义 cin 的类型和 string 类型的命名空间中查找匹配的函数。因此,它在命名空间 std 中查找并找到由 string 类型定义的 getline 函数。

The reason that functions are made visible if they have a parameter of the class type is to allow nonmember functions that are conceptually part of a class' interface to be used without requiring a separate using declaration. Being able to use nonmember operations is particularly useful for operator functions.

如果函数具有类类型形参就使得函数可见,其原因在于,允许无须单独的 using 声明就可以使用概念上作为类接口组成部分的非成员函数。能够使用非成员操作对操作符函数特别有用。

For example, consider the following simple program:

例如,考虑下面的简单程序:


    std::string s;
    cin >> s;

In absence of this exception to the lookup rules, we would have to write either:

如果没有查找规则的这个例外,我们将必须编写下面二者之一:


    using std::operator>>;        // need to allow cin >> s
    std::operator>>(std::cin, s); // ok: explicitly use std::>>

Either of these declarations is awkward and would make simple uses of strings and the IO library more complicated.

这两个声明都不方便使用,而且可能使 string 和 IO 库的使用变得更复杂。

Implicit Friend Declarations and Namespaces
隐式友元声明与命名空间

Recall that when a class declares a friend function (Section 12.5, p. 465), a declaration for the function need not be visible. If there isn't a declaration already visible, then the friend declaration has the effect of putting a declaration for that function or class into the surrounding scope. If a class is defined inside a namespace, then an otherwise undeclared friend function is declared in the same namespace:

回忆一下,当一个类声明友元函数(第 12.5 节)的时候,函数的声明不必是可见的。如果不存在可见的声明,那么,友元声明具有将该函数或类的声明放入外围作用域的效果。如果类在命名空间内部定义,则没有另外声明的友元函数在同一命名空间中声明。


    namespace A {
        class C {
            friend void f(const C&); // makes f a member of namespace A
        };
    }

Because the friend takes an argument of a class type and is implicitly declared in the same namespace as the class, it can be used without using an explicit name-space qualifier:

因为该友元接受类类型实参并与类隐式声明在同一命名空间中,所以使用它时可以无须使用显式命名空间限定符:


    // f2 defined at global scope
    void f2()
    {
         A::C cobj;
         f(cobj); // calls A::f
    }

17.2.6. Overloading and Namespaces

17.2.6. 重载与命名空间

As we've seen, each namespace maintains its own scope. As a consequence, functions that are members of two distinct namespaces do not overload one another. However, a given namespace can contain a set of overloaded function members.

正如我们所见,每个命名空间维持自己的作用域,因此,作为两个不同命名空间的成员的函数不能互相重载。但是,给定命名空间可以包含一组重载函数成员。

In general, function matching (Section 7.8.2, p. 269) within a namespace happens in the same manner as we've already seen:

一般而言,命名空间内部的函数匹配(第 7.8.2 节)以与我们已经见过的方式相同的方式进行:

  1. Find the set of candidate functions. A function is a candidate if a declaration for it is visible at the time of the call and if it has the same name as the called function.

    找到候选函数集。如果一个函数在调用时其声明可见并且与被调用函数同名,这个函数就是候选者。

  2. Select the viable functions from the set of candidates. A function is viable if it has the same number of parameters as the call has arguments and if each parameter could be matched by the corresponding argument.

    从候选集中选择可行函数。如果函数的形参数目与函数调用的实参数目相同,并且每个形参都可用对应实参匹配,这个函数就是可行的。

  3. Select the single best match from the viable set and generate code to call that function. If the viable set is empty, then the call is in error, having no match. If the viable set is nonempty and there is no best match, then the call is ambiguous.

    从可行集合中选择一个最佳匹配,并产生代码调用该函数。如果可行集合为空,则调用出错,没有匹配;如果可行集合非空且没有最佳匹配,则调用有二义性。

Candidate Functions and Namespaces
候选函数与命名空间

Namespaces can have two impacts on function matching. One of these should be obvious: A using declaration or directive can add functions to the candidate set. The other is much more subtle.

命名空间对函数匹配有两个影响。一个影响是明显的:using 声明或 using 指示可以将函数加到候选集合。另一个影响则微妙得多。

As we saw in the previous section, name lookup for functions that have one or more class-type parameters includes the namespace in which each parameter's class is defined. This rule also impacts how we determine the candidate set. Each namespace that defines a class used as a parameter (and those that define its base class(es)) is searched for candidate functions. Any functions in those namespaces that have the same name as the called function are added to the candidate set. These functions are added even though they otherwise are not visible at the point of the call. Functions with the matching name in those namespaces are added to the candidate set:

正如前节所见,有一个或多个类类型形参的函数的名字查找包括定义每个形参类型的命名空间。这个规则还影响怎样确定候选集合,为找候选函数而查找定义形参类(以及定义其基类)的每个命名空间,将那些命名空间中任意与被调用函数名字相同的函数加入候选集合。即使这些函数在调用点不可见,也将之加入候选集合。将那些命名空间中带有匹配名字的函数加入候选集合:


    namespace NS {
        class Item_base { /* ... */ };
        void display(const Item_base&) { }
    }
    // Bulk_item's base class is declared in namespace NS
    class Bulk_item : public NS::Item_base { };
    int main() {
        Bulk_item book1;
        display(book1);
        return 0;
    }

The argument, book1, to the display function has class type Bulk_item. The candidate functions for the call to display are not only the functions with declarations that are visible where the function display is called, but also the functions in the namespace where the class Bulk_item and its base class Item_base are declared. The function display(const Item_base&) declared in namespace NS is added to the set of candidate functions.

display 函数的实参 book1 具有类类型 Bulk_itemdisplay 调用的候选函数不仅是在调用 display 函数的地方其声明可见的函数,还包括声明 Bulk_item 类及其基类 Item_base 的命名空间中的函数。命名空间 NS 中声明的函数 display(const Item_base&) 被加到候选函数集合中。

Overloading and using Declarations
重载与 using 声明

A using declaration declares a name. As we saw in Section 15.5.3 (p. 592), there is no way to write a using declaration to refer to a specific function declaration:

using 声明声明一个名字。正如第 15.5.3 节所见,没有办法编写 using 声明来引用特定函数声明:


    using NS::print(int); // error: cannot specify parameter list
    using NS::print;      // ok: using declarations specify names only

If a function is overloaded within a namespace, then a using declaration for the name of that function declares all the functions with that name. If there are print functions for int and double in the namespace NS, then a using declaration for NS::print makes both functions visible in the current scope.

如果命名空间内部的函数是重载的,那么,该函数名字的 using 声明声明了所有具有该名字的函数。如果命名空间 NS 中有用于 intdouble 的函数,则 NS::printusing 声明使得两个函数都在当前作用域中可见。

A using declaration incorporates all versions of an overloaded function to ensure that the interface of the namespace is not violated. The author of a library provided different functions for a reason. Allowing users to selectively ignore some but not all of the functions from a set of overloaded functions could lead to surprising program behavior.

一个 using 声明包括重载函数的所有版本以保证不违反命名空间的接口。库作者为一个理由提供不同函数,允许用户选择性地忽略重载函数集合中的某些但不是全部函数,可能会导致奇怪的程序行为。

The functions introduced by a using declaration overload any other declarations of the functions with the same name already present in the scope where the using declaration appears.

using 声明引入的函数,重载出现 using 声明的作用域中的任意其他同名函数的声明。

If the using declaration introduces a function in a scope that already has a function of the same name with the same parameter list, then the using declaration is in error. Otherwise, the using declaration defines additional overloaded instances of the given name. The effect is to increase the set of candidate functions.

如果 using 声明在已经有同名且带相同形参表的函数的作用域中引入函数,则 using 声明出错,否则,using 定义给定名字的另一重载实例,效果是增大候选函数集合。

Overloading and using Directives
重载与 using 指示

A using directive lifts the namespace members into the enclosing scope. If a namespace function has the same name as a function declared in the scope at which the namespace is placed, then the namespace member is added to the overload set:

using 指示将命名空间成员提升到外围作用域。如果命名空间函数与命名空间所在的作用域中声明的函数同名,就将命名空间成员加到重载集合中:


    namespace libs_R_us {
        extern void print(int);
        extern void print(double);
    }
    void print(const std::string &);
    // using directive:
    using namespace libs_R_us;
    // using directive added names to the candidate set for calls to print:
    // print(int) from libs_R_us
    // print(double) from libs_R_us
    // print(const std::string &) declared explicitly
    void fooBar(int ival)
    {
         print("Value: "); // calls global print(const string &)
         print(ival);      // calls libs_R_us::print(int)
    }

Overloading across Multiple using Directives
跨越多个 using 指示的重载

If many using directives are present, then the names from each namespace become part of the candidate set:

如果存在许多 using 指示,则来自每个命名空间的名字成为候选集合的组成部分:


    namespace AW {
        int print(int);
    }
    namespace Primer {
        double print(double);
    }
    // using directives:
    // form an overload set of functions from different namespaces
    using namespace AW;
    using namespace Primer;
    long double print(long double);
    int main() {
        print(1);   // calls AW::print(int)
        print(3.1); // calls Primer::print(double)
        return 0;
    }

The overload set for the function print in global scope contains the functions print(int), print(double), and print(long double). These functions are all part of the overload set considered for the function calls in main, even though these functions were originally declared in different namespace scopes.

全局作用域中 print 函数的重载集合包含函数 print(int)、print(double)print(long double),即使这些函数原来在不同的命名空间中声明,它们都是为 main 中函数调用考虑的重载集合的组成部分。

Exercises Section 17.2.6

Exercise 17.22:

Given the following code, determine which function, if any, matches the call to compute. List the candidate and viable functions. What type conversion sequence, if any, is applied to the argument to match the parameter in each viable function?

给定下面的代码,如果有,确定哪个函数与 compute 函数的调用匹配,列出候选函数与可行函数。如果有,对实参应用什么类型转换序列,以匹配每个可行函数的形参?

    namespace primerLib {
        void compute();
        void compute(const void *);
    }
    using primerLib::compute;
    void compute(int);
    void compute(double, double = 3.4);
    void compute(char*, char* = 0);

    int main()
    {
        compute(0);
        return 0;
    }

What would happen if the using declaration were located in main before the call to compute? Answer the same questions as before.

如果 main 中的 using 声明放在 compute 调用之前,会发生什么情况?回答与前面相同的问题。


17.2.7. Namespaces and Templates

17.2.7. 命名空间与模板

Declaring a template within a namespace impacts how template specializations (Section 16.6, p. 671) are declared: An explicit specialization of a template must be declared in the namespace in which the generic template is defined. Otherwise, the specialization would have a different name than the template it specialized.

在命名空间内部声明模板影响着怎样声明模板特化(第 16.6 节):模板的显式特化必须在定义通用模板的命名空间中声明,否则,该特化将与它所特化的模板不同名。

There are two ways to define a specialization: One is to reopen the namespace and add the definition of the specialization, which we can do because namespace definitions are discontiguous, Alternatively, we could define the specialization in the same way that we can define any namespace member outside its namespace definition: by defining the specialization using the template name qualified by the name of the namespace.

有两种定义特化的方式:一种是重新打开命名空间并加入特化的定义,可以这样做是因为命名空间定义是不连续的;或者,可以用与在命名空间定义外部定义命名空间成员相同的方式来定义特化:使用由命名空间名字限定的模板名定义特化。

To provide our own specializations of templates defined in a namespace, we must ensure that the specialization definition is defined as being in the namespace containing the original template definition.

为了提供命名空间中所定义模板的自己的特化,必须保证在包含原始模板定义的命名空间中定义特化。



        
Team LiB
Previous Section Next Section