18

在他的《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

4 回答 4

6

大多数情况下,我想知道有什么好处(正如 Raymond Chen 所说,每个功能都以 -100 分开始)。但是,我要提供一个对位:Luabind,它确实使用了类似这样的东西。见luabind/object.hpp,它基本上说:

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

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

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

namespace luabind { class object; }

你需要这样做:

namespace luabind { namespace adl { class object; } }

因此,抽象很快就会泄露,因为毕竟用户确实需要知道这个神奇的adl命名空间。

于 2013-06-01T10:08:45.387 回答
3

很简单,因为没人学。大多数程序员都学习过 Java 风格的 OOP,更常见的是 C++ 代码将命名空间风格的 API 封装到class.

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

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

namespace api {

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

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

}

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

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

因此,鉴于上述情况,您可以这样做:

api::array x;

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

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

于 2013-06-01T12:12:42.527 回答
0

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

  • 地图.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 分成两个文件。

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

  • 地图.h:

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

在那种情况下,如果它们都平等地暴露给所有客户端/包含器,我想将它们放在两个相同抽象级别的命名空间中的意义何在?

于 2013-06-01T10:03:54.207 回答
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::.

于 2017-05-25T03:33:18.237 回答