问题描述
简介:是否安全可以
命名空间Foo {
#includebar
}
在你blithely说不,我想我有一些规则,允许它相当安全。 / p>
但我不喜欢他们,因为他们需要包含器单独包括所有需要的全局范围头。虽然这可能是可以容忍的,如果我们想象在命名空间中包含只是一个特殊的管理功能。
总的来说,externs和forward声明不能很好地工作
所以我想知道我在问
a)其他什么东西
b)是否有更好的方法
== A [[Header-only library]] ==
我喜欢写图书馆。 [[Header-only libraries and linker libraries]。
例如
#includeValid.hpp
为简单的包装器类型定义模板有效。
(不要陷入你应该使用一些标准库,而不是你自己的,这是一个exanple。我dunno如果Boost或C + +标准化了,我一直在使用包装器,因为模板被添加到C ++。)
此外,让我们说,它是一个标题唯一的库, hpp,
a打印函数
std :: string to_string(const Valid& v){
std :: ostringstream oss;
if (v.valid()){oss<&v;}
else {invalid;}
return oss.str();
}
因为我认为这是正确的事情,
我有Valid.hpp包含头部它依赖于:
Valid.hpp:
/ pre>
#include< iostream>
#include< sstream>
template< typename T& ;
class Valid {
private:
T value_;
bool valid_
...
};
...
std :: string to_string(const Valid< T>& v){...
到目前为止,还不错。
我可以直接使用有效。
==名称冲突 - 试图在命名空间内使用include
是碰撞。
有时别人有自己的Valid。
命名空间的救援,对不对?但我不想改变我所有的现有代码使用命名空间。
所以,我很想在一个有冲突的新项目中做。命名空间AG {
namespace Wrapper {
#includelib / AG / Wrapper / Valid.hpp
}
}
AG :: Wrapper :: Valid< T& foo_v;
...
问题:包含的标题不再是独立的。里面定义的一切都没有放在
命名空间AG :: Wrapper里面。
不难修复。
我们必须do包括Valid.hpp依赖的所有顶级库。
如果他们包含警卫,它们不会被重新包括。#include< iostream>
#include
namespace AG {
namespace Wrapper {
#includelib / AG / Wrapper / Valid.hpp
}
}
AG :: Wrapper :: Valid< T> foo_v;
...
但它不再是独立的。 : - (
更糟糕的是,有时只有头文件的库包含外部声明和向前声明的外部的东西
这些声明也放在命名空间内。
特别是,如果extern declarayion在命名空间中定义的函数内部。
有时我们使用extern和forward声明,文件
这些包含在命名空间中。
问:有更好的方法吗?
== :: does not do it ==
提示:::不会这样做至少不是所有的时间,而不是在gcc 4.7.2。
(Gcc的行为在这一段时间内发生了变化,Gcc 4.1.2的行为不同。)
/ p>
类型var;
命名空间Foo {
void bar(){
extern :: Type :: var ;;
extern :: Type :: Foo :: bar;
extern :: Type :: foo :: bar; //看到歧义?
} ;
但这不仅仅是模糊。
int var;
命名空间Foo {
void bar(){
extern int var;
};
works - Foo :: bar'svar等于:: var。
但它只有在命名空间之外的声明才有效。
以下不起作用
header
int var;
cpp
命名空间Foo {
void bar(){
extern int var;
}
}
虽然如下:
int var;
cpp
void bar(){
extern int var;
}
}
基本上,这就是说
它不是一个简单的重构将函数放在命名空间。
在一段代码中包含命名空间,
无论是否包含#include'd,
都不够。
即使你
==对包含在命名空间内的意见==
Stackoverflow的人似乎反对将#includes放在命名空间内:
例如:
总体问题:
有什么办法,从命名空间内深入,
说现在这里有一些名字,我依赖于外部世界,而不是命名空间。
我想能够说
命名空间A {
void foo(){
// ---这里是一个参考gloal范围extreren ...
解决方案我知道这是一个老问题,但我想给出更详细的答案。
这里只是一些东西,如果你在命名空间中包含一个标题,可能会出错。
头包括其他头,然后也包括在命名空间内。然后一个不同的地方也想包括这些头,但从外面的命名空间。因为头部包含保护,只有一个包含实际上生效,并且头部中定义的东西的实际命名空间突然巧妙地取决于包括其他头部的顺序。
头部或其任何包含的头部,期望在全局命名空间中。例如,标准库头文件通常(为了避免冲突)引用其他标准的东西(或实现细节)为
:: std :: other_stuff
,即expectstd
直接在全局命名空间中。如果你在命名空间中包含头,情况就不一样了。这个东西的名称查找将失败,标题将不再编译。它不只是标准的标题;我敢肯定有一些例子,例如。
如果您通过确保首先包含所有其他标题来处理第一个问题,使用完全限定名,事情仍然可能出错。有些库需要其他库专门处理他们的东西。例如,库可能想要专门化
std :: swap
,std :: hash
或std :: less
为它自己的类型。 (你可以重载std :: swap
,但你不能这样做std :: hash
code> std :: less 。)这样做的方法是关闭你的库特定的命名空间,打开命名空间std
专业化。除非库的头包含在任意深度嵌套的命名空间中,否则它不能关闭这些命名空间。命名空间std
尝试打开不会是:: std
,但:: YourStuff :: std
,它可能不包含要专门化的任何主要模板,即使它是,也仍然是错误的事情。
最后,命名空间中的东西只有不同的名字。如果你的库不是头文件,但有一个编译的部分,编译的部分可能没有嵌套在命名空间中的所有东西,所以库中的东西不同于你刚才包含的东西。换句话说,您的程序将无法链接。
因此,在理论上,在一个命名空间,但他们很讨厌使用(必须冒泡所有依赖包含者)和非常受限(不能使用完全限定名或专门化的东西在另一个库的命名空间,必须是头)。所以不要这样做。
但是你有一个不使用命名空间的旧库,而且你想更新它使用它们不破坏所有的旧代码。
:
$ b首先,在您的库的include目录中添加一个子目录。调用它命名空间或类似的东西。接下来,将所有头文件移动到该目录中,并将其内容封装在命名空间中。
然后,将转发头文件添加到基本目录。对于库中的每个文件,您添加的转发器如下所示:
#ifndef YOURLIB_LEGACY_THE_HEADER_H
#define YOURLIB_LEGACY_THE_HEADER_H
#includenamespaced / the_header.h
使用命名空间yourlib;
#endif
现在旧代码应该始终工作
对于新代码,诀窍是不包括namespaced / the_header.h,而是改变项目设置,使include目录指向命名空间子目录而不是库根。然后你可以简单地包含the_header.h并获得命名空间的版本。
BRIEF: is it ever safe to do
namespace Foo { #include "bar" }
Before you blithely say no, I think I have some rules that allow it fairly safely.
But I don't like them, since they require the includer to separately include all global scope headers needed. Although this mightr be tolerable, if we imagine including within a namespace to be just a special management feature.
And overall, externs and forward declarations just don't work well from within namespaces.
So I gues I am asking
a) What other gotchas
b) is there a better way
== A [[Header-only library]] ==
I like writing libraries. [[Header-only libraries and linker libraries]].
E.g.
#include "Valid.hpp"
defines a template Valid, for a simple wrapper type.
(Don't get bogged down in "You should use some standard library for this rather than your own. This is an exanple. I dunno if Boost or C++ have yet standardized this. I have been using wrappers since templates were added to C++.)
Also, let us say, it is a header only library, that defines, in Valid.hpp,a print function
std::string to_string( const Valid& v ) { std::ostringstream oss; if( v.valid() ) { oss << v; } else { "invalid"; } return oss.str(); }
And because I think it is the right thing to do,I have Valid.hpp include the headers it depends on:
Valid.hpp: #include <iostream> #include <sstream> template<typename T> class Valid { private: T value_; bool valid_ ... }; ... std::string to_string( const Valid<T>& v ) { ...
So far, so good.
I can use Valid straightforwardly.
== Name collision - trying to use include within namespace to work around ==
But sometimes there is a collision.Sometimes somebody else has their own Valid.
Namespaces to the rescue, right? But I don't want to change all of my existing code to use the namespace.So, I am tempted, in a new project that has a collision, to do
namespace AG { namespace Wrapper { #include "lib/AG/Wrapper/Valid.hpp" } } AG::Wrapper::Valid<T> foo_v; ...
PROBLEM: the headers included are no longer freestanding. Everything defined inside is no placed insidenamespace AG::Wrapper.
It's not hard to "fix".
Al we "must" do is include all the top level libraries that Valid.hpp depends on.If they have include guards, they will not be re-included.#include <iostream> #include <sstream> namespace AG { namespace Wrapper { #include "lib/AG/Wrapper/Valid.hpp" } } AG::Wrapper::Valid<T> foo_v; ...
But it is no longer freestanding. :-(
Worse, sometimes the header-only library contains extern declarations and forward declarations of stuff outside itself.These declarations get placed inside the namespace too.In particular, if the extern declarayion is inside a function defined in the namespace.
I.e. sometimes we use extern and forward declarations, rather than included an entire header file.These get included in the namespace.
Q: is there a better way?
== :: doesn't do it ==
Hint: :: doesn't do it. At least not all the time, not in gcc 4.7.2.
(Gcc's behavior in this has changed over time. Gcc 4.1.2 behaved differently.)E.g.
Type var; namespace Foo { void bar() { extern ::Type ::var;; extern ::Type ::Foo::bar; extern ::Type::foo ::bar; // see the ambiguity? };
But it's not just the ambiguity.
int var;
namespace Foo { void bar() { extern int var; };
works - Foo::bar'svar is equal to ::var.
But it only works because of the declaration outside the namespace.
The following doesn't work
header int var; cpp namespace Foo { void bar() { extern int var; } }
although the following does:
header int var; cpp void bar() { extern int var; } }
Basically, what this amounts to saying is thatit is not a trivial refactoring to put functions inside a namespace.Wrapping a namespace around a chunk of code,whether or not it is #include'd,is not a sufficient.... at least not if there are extern or forward declarations.
And even if you
== Opinion against putting includes within namespaces ==
Stackoverflow folks seem to be against putting #includes inside namespaces:
E.g. How to use class defined in a separate header within a namespace:
Overall question:
Is there any way, from deep within a namespace,to say "and now here are some names that I am depending on from the outside world, not within the namespace."?
I.e. I would like to be able to say
namespace A { void foo() { // --- here is a reference to gloal scope extreren ...
解决方案I know this is an old question, but I want to give a more detailed answer anyway. Also, give a real answer to the underlying problem.
Here's just a few things that can go wrong if you include a header from within a namespace.
The header includes other headers, which are then also included from within the namespace. Then a different place also wants to include these headers, but from outside the namespace. Because the headers have include guards, only one of the includes actually goes in effect, and the actual namespace of the stuff defined in the headers suddenly subtly depends on the order you include other headers.
The header, or any of its included headers, expects to be in the global namespace. For example, standard library headers will very often (in order to avoid conflicts) refer to other standard stuff (or implementation details) as
::std::other_stuff
, i.e. expectstd
to be directly in the global namespace. If you include the header from within a namespace, that's no longer the case. The name lookup for this stuff will fail and the header will no longer compile. And it's not just standard headers; I'm sure there are some instances of this e.g. in the Boost headers too.If you take care of the first problem by ensuring that all other headers are included first, and the second problem by making sure no fully qualified names are used, things can still go wrong. Some libraries require that other libraries specialize their stuff. For example, a library might want to specialize
std::swap
,std::hash
orstd::less
for its own type. (You can overloadstd::swap
instead, but you can't do that forstd::hash
andstd::less
.) The way to do this is close your library-specific namespace, open namespacestd
, and put the specialization there. Except if the header of the library is included in arbitrarily deeply nested namespaces, it cannot close those namespaces. The namespacestd
it attempts to open won't be::std
, but::YourStuff::std
, which probably doesn't contain any primary template to specialize, and even if it did, that would still be the wrong thing to do.Finally, things in a namespace simply have different names than things outside. If your library isn't header-only but has a compiled part, the compiled part probably didn't nest everything in the namespace, so the stuff in the library has different names than the stuff you just included. In other words, your program will fail to link.
So in theory, you can design headers that work when included within a namespace, but they're annoying to use (have to bubble up all dependencies to the includer) and very restricted (can't use fully qualified names or specialize stuff in another library's namespace, must be header-only). So don't do it.
But you have an old library that doesn't use namespaces, and you want to update it to use them without breaking all your old code. Here's what you should do:
First, you add a subdirectory to your library's include directory. Call it "namespaced" or something like that. Next, move all the headers into that directory and wrap their contents in a namespace.
Then you add forwarding headers to the base directory. For each file in the library, you add a forwarder that looks like this:
#ifndef YOURLIB_LEGACY_THE_HEADER_H #define YOURLIB_LEGACY_THE_HEADER_H #include "namespaced/the_header.h" using namespace yourlib; #endif
Now the old code should just work the way it always did.
For new code, the trick is not to include "namespaced/the_header.h", but instead change the project settings so that the include directory points at the namespaced subdirectory instead of the library root. Then you can simply include "the_header.h" and get the namespaced version.
这篇关于#include在命名空间内,“嵌入”在命名空间中预写东西的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!