为什么命名空间组合很少使用?

时间:2013-06-01 09:50:29

标签: c++

在他的书“ C ++编程语言”(第三版)中,Stroustrup教授在自己的命名空间中定义单个组件并将它们导入通用命名空间。

例如:

namespace array_api {    
    struct array {};    
    void print(const array&) { }
}

namespace list_api {
    struct list {};        
    void print(const list&) { }
}

namespace api {
    using array_api::array;
    using list_api::list;
}

我看起来很有趣,但我从未见过这种方法在实践中使用过。

为什么这种技术几乎从未使用过?

4 个答案:

答案 0 :(得分:6)

大多数情况下,我想知道它会带来什么好处(正如Raymond Chen所说,每个功能都以-100点开头)。但是,我有一个对立面提供:Luabind,它确实使用了这样的东西。请参阅luabind/object.hpp,其实质上是:

namespace luabind {
  namespace adl {
    class object {
    };
  }
  using adl::object;
}

仅从名称我们可以推断出动机:支持依赖于参数的查找。鉴于用户知道的luabind::object实际上是luabind::adl::object,编译器将从luabind::adl命名空间自动发现相关函数。然而,用户可能不需要以非常明确的方式知道的那些函数不会“污染”主luabind命名空间。所以这很好,我想。

但你知道什么不好吗?转发声明其中一个类。这失败了:

namespace luabind { class object; }

你需要这样做:

namespace luabind { namespace adl { class object; } }

因此抽象很快就会泄漏,因为用户确实需要了解这个神奇的adl命名空间。

答案 1 :(得分:3)

仅仅是因为没有人学到它。大多数程序员都学习Java风格的OOP,并且更常见的是看到C ++代码将命名空间风格的API封装到class中。

C ++具有参数依赖函数查找(ADL),它允许它根据API调用的参数类型的名称空间从API中选择函数。它是一种强大的机制,允许您绕过大部分OOP样板并保留其优势。但是,并没有真正教给初学者,因为没有特别的理由。

对于它的价值,你的例子大致相当于这个浓缩版本:

namespace api {

struct array {
    friend void print(const array&) { }
};

struct list {       
    friend void print(const list&) { }
};

}

当您在一个类中将函数定义为friend时,它不属于类,而是属于封闭的命名空间。此外,它的名称仅在类中可用,而不是封闭的名称空间,除非使用类作为参数调用它。

使用array_apilist_api命名空间污染全局命名空间是个坏主意。最好像我的例子那样建立层次结构。

所以,鉴于上述情况,你可以这样做:

api::array x;

print( x ); // ADL finds api::print defined inside api::array

实际上,这就是iostream风格operator <<的工作原理。您不需要using声明(或指令)来打印库中的对象。

答案 2 :(得分:0)

我猜是因为它减少了封装。在您的示例中,当您编写map_api以转到api.h并将其导入api命名空间时,这将是一个正确的痛苦。有一个导入每个子命名空间的大标头api.h并不完全包括最小化。

  • map.h:

    namespace map_api { /* fns */ }
    
  • api.h:

    #include <map.h>
    namespace api { using map_api::map; }
    

或者,考虑其他标题布局:如果导入api名称空间发生在另一个文件中:

  • map_internal.h:

    namespace map_api { /* fns */ }
    
  • map_api.h:

    #include <map_internal.h>
    namespace api { using map_api::map; }
    

这也很痛苦,不得不将api分成两个这样的文件。

最后的可能性是在一个文件中完成所有操作:

  • map.h:

    namespace map_api { /* fns */ }
    namespace api { using map_api::map; }
    

在这种情况下,如果它们同时暴露给所有客户端/包含者,那么我想知道如何将事物放在同一抽象级别的两个名称空间中。

答案 3 :(得分:0)

我觉得其他答案遗漏了构建命名空间的第一个,最简单,最有用的方法:只使用你需要的东西

namespace Needed {                                                                                                                                                                            
  using std::string;                                                                                                                                                                          
  using std::bind;                                                                                                                                                                            
  using std::function;                                                                                                                                                                        
  using std::cout;                                                                                                                                                                            
  using std::endl;                                                                                                                                                                            
  using namespace std::placeholders;                                                                                                                                                          
}                                                                                                                                                                                             


int main(int argc, char* argv[])                                                                                                                                                              
{                                                                                                                                                                                             

  /*  using namespace std;                                                                                                                                                                    
      would avoid all these individual using clauses,                                                                                                                     
      but this way only these are included in the global                                                                                                                                      
      namespace.                                                                                                                                                                          
  */                                                                                                                                                                                          

 using namespace Needed;  // pulls in the composition

 string s("Now I have the namespace(s) I need,");

 string t("But not the ones I don't.");

 cout << s << "\n" << t << endl;                                                                                               

 // ...

因此,避免了与STL的其他部分的任何冲突,以及 需要在std::前面加上常用的函数。