在他的书“ 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;
}
我看起来很有趣,但我从未见过这种方法在实践中使用过。
为什么这种技术几乎从未使用过?
答案 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_api
和list_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::
前面加上常用的函数。