命名空间

在.cc文件中,提倡使用不具名命名空间。如果使用具名命名空间,基于项目或路径名称而不是using指示符 (例如 using namespace foo) 禁止使用内联 (inline) 命名空间 。命名空间内不用缩进。 定义:命名空间可以将全局作用域 (global scope) 划分为独立的、有名字的作用域, 因此可以有效防止全局作用域中的命名冲突 (name collision). 优点:命名空间可以避免大型程序中的命名冲突, 同时代码可以继续使用简短的名称.

举例来说, 若两个项目的全局作用域中都有一个叫 Foo 的类 (class), 这两个符号 (symbol) 会在编译或运行时发生冲突. 如果每个项目在不同的命名空间中放置代码, project1::Fooproject2::Foo 就是截然不同的符号, 不会冲突.

内联命名空间会自动把其中的标识符置入外层作用域, 比如:

namespace outer {
inline namespace inner {
    void foo();
}  // namespace inner
}  // namespace outer

此时表达式 outer::inner::foo()outer::foo() 等效. 内联命名空间的主要用途是保持不同 ABI 版本之间的兼容性。 缺点:不具名命名空间容易违背C++中唯一定义的原则.部分情景下, 我们必须多次使用完全限定名称 (fully-qualified name) 来引用符号. 此时多层嵌套的命名空间会让代码冗长. 。 结论:建议按如下方法使用命名空间:

  • 遵守 命名空间命名 规则.

  • 像前面的例子一样, 用注释给命名空间收尾. (译者注: 注明命名空间的名字.)

  • 在导入语句、 gflags 声明/定义以及其他命名空间的类的前向声明 (forward declaration) 之后, 用命名空间包裹整个源代码文件:

    // .h 文件
    namespace mynamespace {
    
    // 所有声明都位于命名空间中.
    // 注意没有缩进.
    class MyClass {
        public:
        ...
        void Foo();
    };
    
    }  // namespace mynamespace
    // .cc 文件
    namespace mynamespace {
    
    // 函数定义位于命名空间中.
    void MyClass::Foo() {
        ...
    }
    
    }  // namespace mynamespace

    更复杂的 .cc 文件有更多细节, 比如旗标 (flag) 或 using 声明.

    #include "a.h"
    
    DEFINE_FLAG(bool, someflag, false, "某个旗标");
    
    namespace mynamespace {
    
    using ::foo::Bar;
    
    ...命名空间内的代码...  // 代码紧贴左边框.
    
    }  // namespace mynamespace
  • 若要将自动生成的 proto 消息代码放入命名空间, 可以在 .proto 文件中使用 package 修饰符 (specifier). 参见 Protocol Buffer 的包.

  • 不要在 std 命名空间内声明任何东西. 不要前向声明 (forward declare) 标准库的类. 在 std 命名空间内声明实体是未定义行为 (undefined behavior), 也就是会损害可移植性. 若要声明标准库的实体, 应该导入对应的头文件.

  • 禁止使用 using 指令 引入命名空间的所有符号。

    // 禁止: 这会污染命名空间.
    using namespace foo;
  • 除了在明显标注为内部使用的命名空间内, 不要让头文件引入命名空间别名 (namespace alias). 这是因为头文件的命名空间中引入的任何东西都是该文件的公开 API. 正确示例:

    // 在 .cc 中, 用别名缩略常用的名称.
    namespace baz = ::foo::bar::baz;
    // 在 .h 中, 用别名缩略常用的命名空间.
    namespace librarian {
    namespace impl {  // 仅限内部使用, 不是 API.
    namespace sidetable = ::pipeline_diagnostics::sidetable;
    }  // namespace impl
    
    inline void my_inline_function() {
      // 一个函数 (f或方法) 中的局部别名.
      namespace baz = ::foo::bar::baz;
      ...
    }
    }  // namespace librarian
  • 禁止内联命名空间.

  • 如果命名空间的名称包含 “internal”, 代表用户不应该使用这些 API.

    // Absl 以外的代码不应该使用这一内部符号.
    using ::absl::container_internal::ImplementationDetail;
  • 我们鼓励新的代码使用单行的嵌套命名空间声明, 但不强制要求.

    译者注: 例如

    namespace foo::bar {
    ...
    }  // namespace foo::bar