gpt4 book ai didi

c++ - #includes 在命名空间中,到 "embed"命名空间中的预写内容

转载 作者:太空狗 更新时间:2023-10-29 19:53:23 25 4
gpt4 key购买 nike

简介:这样做是否安全

 namespace Foo {
#include "bar"
}

在你愉快地说不之前,我想我有一些规则可以相当安全地允许它。

但我不喜欢它们,因为它们要求包含程序单独包含所需的所有全局范围 header 。尽管这可能是可以容忍的,但如果我们将 namespace 包含在内只是一种特殊的管理功能。

总的来说,externs 和前向声明在命名空间内不能很好地工作。

所以我想我在问

a) 还有什么问题

b) 有没有更好的方法

== A [[仅标题库]] ==

我喜欢写图书馆。 [[仅头文件库和链接器库]]。

例如。
#include "Valid.hpp"

为简单的包装器类型定义模板 Valid。

(不要陷入“你应该为此使用一些标准库而不是你自己的库。这是一个例子。我不知道 Boost 或 C++ 是否已经对此进行了标准化。自从模板被添加到 C++ 以来,我一直在使用包装器。 )

另外,让我们说,它是一个只有头文件的库,它在 Valid.hpp 中定义,
打印功能

std::string to_string(const Valid& v) {
std::ostringstream oss;
if( v.valid() ) { oss << v; }
其他{“无效”; }
返回 oss.str();
}

因为我认为这是正确的做法,
我有 Valid.hpp 包括它所依赖的 header :
   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 。

== 名称冲突 - 尝试在命名空间中使用包含来解决 ==

但有时会发生碰撞。
有时其他人有自己的 Valid。

命名空间来拯救,对吧?但我不想更改所有现有代码以使用命名空间。
所以,我很想,在一个有碰撞的新项目中,做
   namespace AG {
namespace Wrapper {
#include "lib/AG/Wrapper/Valid.hpp"
}
}

AG::Wrapper::Valid<T> foo_v;

...

问题:包含的标题不再是独立的。里面定义的所有东西都没有放在里面
命名空间 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 声明在命名空间中定义的函数内。

IE。有时我们使用 extern 和 forward 声明,而不是包含整个头文件。
这些被包含在命名空间中。

问:有没有更好的办法?

==::不这样做 ==

提示:::不这样做。至少不是一直如此,不是在 gcc 4.7.2 中。
(Gcc 在这方面的行为随着时间的推移而改变。Gcc 4.1.2 的行为有所不同。)

例如。
 Type var;

namespace Foo {
void bar() {
extern ::Type ::var;;
extern ::Type ::Foo::bar;
extern ::Type::foo ::bar; // see the ambiguity?
};

但这不仅仅是模棱两可。

内部变量;
 namespace Foo {
void bar() {
extern int var;
};

有效 - Foo::bar'svar 等于::var。

但它仅因命名空间之外的声明而起作用。

以下不起作用

标题
内部变量;
cp
命名空间 Foo {
空栏(){
extern int var;
}
}

尽管以下内容确实如此:

标题
内部变量;
cp
空栏(){
extern int var;
}
}

基本上,这相当于说的是
将函数放在命名空间中并不是一个简单的重构。
将命名空间包裹在一段代码中,
无论是否#include'd,
是不够的。
...至少在有外部或前向声明的情况下不会。

而且即使你

== 反对在命名空间内放置包含的意见 ==

Stackoverflow 的人似乎反对将 #includes 放在命名空间中:

例如。 How to use class defined in a separate header within a namespace :

... you should never write a #include inside a namespace. Where "never" means, "unless you're doing something really obscure that I haven't thought of and that justifies it". You want to be able to look at a file, and see what the fully-qualified names are of all the things in it. You can't do that if someone comes along later and sticks an extra namespace on the front by including from inside their namespace. – Steve Jessop Jan 6 '12 at 16:38


总体问题:

有没有办法,从命名空间的深处,
说“现在这里有一些我依赖于外部世界的名称,而不是命名空间内的名称。”?

IE。我想说
namespace A {
void foo() {
// --- here is a reference to gloal scope extreren ...

最佳答案

我知道这是一个老问题,但无论如何我想给出更详细的答案。另外,对根本问题给出真正的答案。

如果您在命名空间中包含 header ,这里只是一些可能出错的事情。

  • header 包括其他 header ,然后这些 header 也包含在命名空间中。然后另一个地方也想包含这些头文件,但是来自命名空间之外。因为头文件有包含保护,实际上只有一个包含文件有效,并且头文件中定义的东西的实际命名空间突然微妙地取决于您包含其他头文件的顺序。
  • header 或其任何包含的 header 应位于全局命名空间中。例如,标准库头文件经常(为了避免冲突)将其他标准内容(或实现细节)引用为 ::std::other_stuff ,即期望 std直接在全局命名空间中。如果您在命名空间中包含 header ,则情况不再如此。这些东西的名称查找将失败并且 header 将不再编译。它不仅仅是标准标题;我确定有一些这样的例子,例如在 Boost 标题中也是如此。
  • 如果您通过确保首先包含所有其他 header 来解决第一个问题,并通过确保不使用完全限定名称来解决第二个问题,那么事情仍然可能出错。一些图书馆要求其他图书馆专门研究他们的东西。例如,图书馆可能想要专门化 std::swap , std::hashstd::less对于它自己的类型。 (您可以重载 std::swap,但不能为 std::hashstd::less 重载。)这样做的方法是关闭特定于库的命名空间,打开命名空间 std ,并将专业放在那里。除非库的头包含在任意深度嵌套的命名空间中,否则它无法关闭这些命名空间。命名空间 std它尝试打开不会是 ::std , 但是 ::YourStuff::std ,它可能不包含任何要专门化的主要模板,即使它包含了,那仍然是错误的做法。
  • 最后,命名空间中的事物与外部事物具有不同的名称。如果您的库不是仅包含头文件而是具有编译部分,则编译部分可能不会将所有内容都嵌套在命名空间中,因此库中的内容与您刚刚包含的内容具有不同的名称。换句话说,您的程序将无法链接。

  • 因此,理论上,您可以设计包含在命名空间中的头文件,但它们使用起来很烦人(必须将所有依赖项冒泡到包含程序中)并且非常受限制(不能使用完全限定名称或专门用于另一个库的命名空间,必须是仅 header )。所以不要这样做。

    但是您有一个不使用 namespace 的旧库,并且您希望更新它以使用它们而不破坏所有旧代码。这是你应该做的:

    首先,您将一个子目录添加到库的包含目录中。称之为“命名空间”或类似的东西。接下来,将所有标题移动到该目录中,并将它们的内容包装在一个命名空间中。

    然后将转发头添加到基本目录。对于库中的每个文件,您添加一个如下所示的转发器:
    #ifndef YOURLIB_LEGACY_THE_HEADER_H
    #define YOURLIB_LEGACY_THE_HEADER_H

    #include "namespaced/the_header.h"
    using namespace yourlib;

    #endif

    现在旧代码应该像往常一样工作。

    对于新代码,诀窍不是包含“namespaced/the_header.h”,而是更改项目设置,使包含目录指向命名空间子目录而不是库根目录。然后您可以简单地包含“the_header.h”并获取命名空间版本。

    关于c++ - #includes 在命名空间中,到 "embed"命名空间中的预写内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14270435/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com