8

我的库使用了几个嵌套的命名空间,布局如下:

Library name
    Class name 1
    Class name 2
    Class name 3
    [...]
    Utilities
        Class name 1
            [...]
        Class name 2
            [...]
        Class name 3
            [...]
        [...]

“实用程序”命名空间包含对每个类的有用扩展,这些扩展不保证包含在实际类本身中。

“库名称”命名空间是必要的,因为它避免了与其他库的广泛冲突,“实用程序”命名空间是必要的,以避免由此类事物引起的歧义类型,并且其中的“类名”命名空间避免名称之间的冲突为类似类编写的实用程序。

尽管如此,这在实践中仍然是一个巨大的麻烦。以以下为例:

MyLibrary::MyContainer<int> Numbers = MyLibrary::Utilities::MyContainer::Insert(OtherContainer, 123, 456);
// Oh God, my eyes...

这让我觉得我做错了什么。有没有更简单的方法来让事情变得井井有条、直观且明确?

4

4 回答 4

15

看看标准库(或 boost)是如何组织的。几乎所有这些都在单个std命名空间内。将所有内容放在自己的命名空间中几乎没有什么好处。

Boost 将大部分内容放在 内部boost,而主要库有一个子命名空间(例如boost::mpl,或boost::filesystem)。aux库通常为内部实现细节定义一个子命名空间。

但是您通常不会看到深度或细粒度的命名空间层次结构,因为使用它们很痛苦,而且几乎没有任何好处。

以下是一些好的经验法则:

与特定类相关的辅助函数应与该类位于同一命名空间中,以使 ADL 能够工作。那么你在调用它时根本不需要限定辅助函数的名称。(就像你如何调用sort而不是std::sort在定义的迭代器上一样std)。

对于其他一切,请记住命名空间的目的是避免名称冲突,仅此而已。因此,您的所有库都应该在一个命名空间中,以避免与用户代码发生冲突,但在该命名空间内,除非您打算引入冲突名称,否则技术上不需要进一步的子命名空间。

您可能希望将库的内部分离到子命名空间中,这样用户就不会意外地从主命名空间中提取它们,类似于 Boost 的aux.

但一般来说,我建议尽可能少的嵌套命名空间。

最后,我倾向于为我的命名空间使用简短、易于输入和易于阅读的名称(同样,std这是一个很好的例子。简短而中肯,几乎总是没有进一步嵌套命名空间,因此您不必经常编写它而感到抽筋,因此它不会使您的源代码过于混乱。)

只是关于辅助函数和 ADL 的第一条规则将允许您的示例改写如下:

MyLibrary::MyContainer<int> Numbers = Insert(OtherContainer, 123, 456);

然后我们可以重命名MyLibraryLib

Lib::MyContainer<int> Numbers = Insert(OtherContainer, 123, 456);

并且您正在处理一些非常易于管理的事情。

不同类的类似效用函数之间不应该有任何冲突。C++ 允许您重载函数,并专门化模板,这样您就可以同时拥有一个Insert(ContainerA)Insert(ContainerB)在同一个命名空间中。

当然,命名空间和类之间的冲突只有在你实际上额外的嵌套命名空间时才可能发生。

请记住,在您的Library命名空间中,独自决定引入哪些名称。因此,您可以通过不创建任何冲突名称来避免名称冲突。将用户代码与库代码分开的命名空间很重要,因为两者可能不知道彼此,因此可能会无意中发生冲突。

但是在您的库中,您可以只给所有内容不冲突的名称。

于 2011-05-02T13:59:43.947 回答
7

如果有什么伤害,请停止这样做。绝对没有必要在 C++ 中使用深度嵌套的命名空间——它们不打算成为架构设备。我自己的代码总是使用单层命名空间。

如果你坚持使用嵌套命名空间,你总是可以为它们创建短别名:

namespace Util = Library::Utility;

然后:

int x = Util::somefunc();   // calls Library::Utility::somefunc()
于 2011-05-02T13:26:19.690 回答
3

头文件中的声明需要命名空间不污染全局命名空间:

MyLibrary::Utilities::MyContainer<int> Numbers;

但是在源文件中你可以使用 usings:

using namespace MyLibrary::Utilities;

...

MyContainer<int> Numbers;
Numbers.Insert(OtherContainer, 123, 456);
于 2011-05-02T13:23:29.767 回答
2

完全限定名称对我来说实际上并没有那么糟糕,但我喜欢在方法和类名称中明确。但using可以帮助解决问题:

您可能可以using namespace MyLibrary在源文件中使用全局范围,使其成为:

MyContainer<int> Numbers = Utilities::MyContainer::Insert(OtherContainer, 123, 456);

然后您可以导入您需要的特定功能:

using MyLibrary::Utilities::MyContainer::Insert

进而 MyContainer<int> Numbers = Insert(OtherContainer, 123, 456);

于 2011-05-02T13:26:15.930 回答