避免过度使用命名空间

时间:2011-05-02 13:16:52

标签: c++ namespaces organization ambiguity

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

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

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

“库名称”命名空间是必需的,因为它避免了与其他库的广泛冲突,“实用程序”命名空间是必要的,以避免由things like this引起的歧义类型,以及内部的“类名”命名空间它避免了为类似类编写的实用程序之间的名称冲突。

尽管如此,在实践中仍然是一个巨大的麻烦。请采取以下措施,例如:

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

这让我觉得我做错了。有没有更简单的方法来保持组织,直观和明确的事情?

4 个答案:

答案 0 :(得分:12)

了解标准库(或boost)的组织方式。几乎所有内容都在单个std命名空间内。通过将所有内容放在自己的命名空间中,几乎无法获得。

Boost将大多数内容放在boost中,而主要库则获取单个子名称空间(boost::mplboost::filesystem,例)。库通常为内部实现细节定义单个aux子名称空间。

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

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

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

对于其他所有内容,请记住命名空间的目的是避免名称冲突,而不是其他。所以你的所有库都应该在 命名空间中,以避免与用户代码冲突,但在该命名空间内,除非你打算引入冲突名称,否则不需要进一步的子命名空间。

您可能希望将库的内部分隔为子命名空间,因此用户不会意外地从主命名空间中选择它们,类似于Boost的aux

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

最后,我倾向于为我的命名空间使用简短,易于输入和易于阅读的名称(同样,std是一个很好的例子。指向,并且几乎总是没有进一步嵌套的命名空间,所以你不必经常编写它,所以它不会使你的源代码混乱太多。)

关于辅助函数和ADL的第一条规则将允许您的示例重写为:

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

然后我们可以将MyLibrary重命名为Lib

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

你可以轻松管理。

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

当然,只有当你真正拥有其他嵌套命名空间时,才能实现命名空间和类之间的冲突。

请记住,在Library命名空间内, 单独指定引入了哪些名称。因此,您可以避免名称冲突,而不是创建任何冲突的名称。将用户代码与库代码分开的命名空间非常重要,因为这两者可能不了解彼此,因此可能会无意中发生冲突。

但是在你的图书馆里,你可以给所有非冲突的名字。

答案 1 :(得分:7)

如果有什么伤害,请停止这样做。绝对不需要在C ++中使用深层嵌套的命名空间 - 它们不是建筑设备。我自己的代码总是使用单级命名空间。

如果您坚持使用嵌套命名空间,则始终可以为它们创建短别名:

namespace Util = Library::Utility;

然后:

int x = Util::somefunc();   // calls Library::Utility::somefunc()

答案 2 :(得分:3)

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

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

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

using namespace MyLibrary::Utilities;

...

MyContainer<int> Numbers;
Numbers.Insert(OtherContainer, 123, 456);

答案 3 :(得分: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);