简介:这样做是否安全
namespace Foo {
#include "bar"
}
在你轻率地拒绝之前,我认为我有一些规则允许它相当安全。
但我不喜欢它们,因为它们要求包装器分别包含所需的所有全局范围标头。虽然这可以容忍,但如果我们想象在命名空间中包含一个特殊的管理功能。
总的来说,externs和forward声明在名称空间内效果不佳。
所以我猜我要问
a)还有什么其他问题
b)有更好的方法
== A [[仅限报头的图书馆]] ==
我喜欢写图书馆。 [[仅限标题库和链接库]]。
E.g。
#include "Valid.hpp"
为简单的包装器类型定义模板Valid。
(不要陷入困境“你应该使用一些标准库而不是你自己的。这是一个例子。我不知道如果Boost或C ++已经标准化了这个。我一直在使用包装器,因为添加了模板到C ++。)
另外,让我们说,它是一个仅头文件库,在Valid.hpp中定义, 打印功能
std :: string to_string(const Valid& v){ std :: ostringstream oss; if(v.valid()){oss<< v; } 别的{“无效”; } return oss.str(); }
因为我认为这是正确的做法, 我有Valid.hpp包括它依赖的头文件:
Valid.hpp:
#include <iostream>
#include <sstream>
template<typename T>
class Valid {
private:
T value_;
bool valid_
...
};
...
std::string to_string( const Valid<T>& v ) { ...
到目前为止,非常好。
我可以直接使用Valid。
==名称冲突 - 尝试在命名空间中使用include来解决==
但有时会发生碰撞。 有时其他人有自己的有效。
拯救命名空间,对吧?但我不想更改所有现有代码以使用命名空间。 所以,我在一个有碰撞的新项目中受到诱惑
namespace AG {
namespace Wrapper {
#include "lib/AG/Wrapper/Valid.hpp"
}
}
AG::Wrapper::Valid<T> foo_v;
...
问题:包含的标题不再是独立的。内部定义的所有内容都没有放在里面 namespace AG :: Wrapper。
“修理”并不难 我们“必须”做的是包括Valid.hpp所依赖的所有顶级库。 如果他们有警卫,他们将不会被重新包括在内。
#include <iostream>
#include <sstream>
namespace AG {
namespace Wrapper {
#include "lib/AG/Wrapper/Valid.hpp"
}
}
AG::Wrapper::Valid<T> foo_v;
...
但它不再是独立的。 : - (
更糟糕的是,有时候只有标题的库包含外部声明和外部声明。 这些声明也放在命名空间内。 特别是,如果extern声明在命名空间中定义的函数内。
即。有时我们使用extern和forward声明,而不是包含整个头文件。 这些包含在命名空间中。
问:有更好的方法吗?== ::不这样做==
提示:::不这样做。至少不是所有的时间,不是在gcc 4.7.2 (Gcc在这方面的行为随着时间的推移而发生了变化.Gcc 4.1.2表现不同。)
E.g。
Type var;
namespace Foo {
void bar() {
extern ::Type ::var;;
extern ::Type ::Foo::bar;
extern ::Type::foo ::bar; // see the ambiguity?
};
但这不仅仅是含糊不清。
int var;
namespace Foo {
void bar() {
extern int var;
};
有效--Foo :: bar'svar等于:: var。
但它只能起作用,因为命名空间之外的声明。
以下不起作用
头 int var; CPP 名称空间Foo { void bar(){ extern int var; } }
虽然如下:
头 int var; CPP void bar(){ extern int var; } }
基本上,这相当于说的是 将函数放在命名空间中并不是一个简单的重构。 围绕一大块代码包装命名空间, 是否#include'd, 还不够。 ......至少不存在外部或前方声明。
即使你
==关于在名称空间内添加包含的意见==
Stackoverflow人员似乎反对在名称空间中放置#includes:
E.g。 How to use class defined in a separate header within a namespace:
...你永远不应该在命名空间中写一个#include。 “永远”的意思是“除非你做的事情真是模糊不清,我没有想到并证明这一点”。您希望能够查看文件,并查看其中所有内容的完全限定名称。如果有人稍后出现并且通过在其名称空间内包含额外的命名空间而无法做到这一点。 - Steve Jessop 12年1月6日16:38
总体问题:
有没有办法,从命名空间的深处, 说“现在这里有一些我依赖于外部世界的名字,而不是名称空间内的名字。”?
即。我想能够说
namespace A {
void foo() {
// --- here is a reference to gloal scope extreren ...
答案 0 :(得分:6)
我知道这是一个老问题,但无论如何我想提供更详细的答案。另外,给出潜在问题的真实答案。
如果你在命名空间中包含一个标题,这里可能会出现一些错误。
标头包含其他标头,然后它们也包含在命名空间中。然后,另一个地方也希望包含这些标头,但是从命名空间外部。因为标题包含防护,所以只有其中一个包含实际生效,标题中定义的东西的实际命名空间突然巧妙地取决于您包含其他标题的顺序。
标头或其任何包含的标头应该位于全局命名空间中。例如,标准库标题通常(为了避免冲突)将其他标准内容(或实现细节)称为::std::other_stuff
,即期望std
直接在全局名称空间中。如果在命名空间中包含标题,则不再是这种情况。这些东西的名称查找将失败,标题将不再编译。它不仅仅是标准标题;我确定有一些这样的例子,例如在Boost标题中也是如此。
如果您通过确保首先包含所有其他标头来解决第一个问题,并通过确保没有使用完全限定名称来解决第二个问题,那么事情仍然可能出错。有些库需要其他库专门化他们的东西。例如,某个库可能希望将std::swap
,std::hash
或std::less
专门用于其自己的类型。 (您可以重载std::swap
,但不能对std::hash
和std::less
执行此操作。)执行此操作的方法是关闭特定于库的命名空间,打开命名空间{{1并将专业化放在那里。除非库的标头包含在任意深度嵌套的命名空间中,否则它无法关闭这些命名空间。它尝试打开的命名空间std
不是std
,而是::std
,它可能不包含任何专门化的主要模板,即使它确实如此,它仍然是做错了。
最后,名称空间中的内容只是名称与外部名称不同。如果您的库不是仅限标题但是具有已编译的部分,则编译的部分可能不会将所有内容嵌套在命名空间中,因此库中的内容与您刚刚包含的内容具有不同的名称。换句话说,您的程序将无法链接。
所以从理论上讲,你可以设计在命名空间中包含时可以工作的头文件,但是它们使用起来很烦人(必须将所有依赖项冒泡到包含器中)并且非常受限制(不能使用完全限定名称或专门化在另一个库的命名空间中的东西,必须是header-only)。所以不要这样做。
但是你有一个不使用命名空间的旧库,你想要更新它以使用它们而不会破坏所有旧代码。这是你应做的事情:
首先,将子目录添加到库的include目录中。称之为“命名空间”或类似的东西。接下来,将所有标头移动到该目录中,并将其内容包装在命名空间中。
然后将转发标头添加到基目录。对于库中的每个文件,添加如下所示的转发器:
::YourStuff::std
现在,旧代码应该像往常一样工作。
对于新代码,技巧不是包含“namespaced / the_header.h”,而是更改项目设置,以便include目录指向命名空间子目录而不是库根。然后你可以简单地包含“the_header.h”并获得命名空间版本。
答案 1 :(得分:4)
我认为这不安全。您将所有包含放入名称空间Foo ... 想象一下你的一些包含来自std命名空间的东西......我无法想象这个烂摊子!
我不会这样做。
答案 2 :(得分:0)
标题文件不是黑盒子。您始终可以查看项目中包含的标头,并查看将其包含在命名空间块中是否安全。或者更好的是,您可以修改标头本身以添加命名空间块。即使标题来自第三方库并在后续版本中发生更改,您的项目中的标题 也不会更改。