#includes 在命名空间中,将预先编写的内容“嵌入”命名空间中

2023-11-23

简而言之:这样做安全吗?

 namespace Foo {
 #include "bar"
 }

在你愉快地拒绝之前,我想我有一些规则可以保证它相当安全。

但我不喜欢它们,因为它们要求包含器单独包含所需的所有全局范围标头。尽管这可能是可以容忍的,但如果我们想象包含在命名空间中只是一个特殊的管理功能。

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

所以我想我是在问

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

因为我认为这是正确的做法 我有 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 。

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

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

命名空间可以拯救你,对吗?但我不想更改所有现有代码以使用命名空间。 所以,我很想在一个有冲突的新项目中做

   namespace AG {
    namespace Wrapper {
    #include "lib/AG/Wrapper/Valid.hpp"
    }
    }

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

    ...

问题:包含的标头不再是独立的。里面定义的所有东西都不放在里面 命名空间 AG:: 包装器。

“修复”并不难。
我们“必须”做的就是包含 Valid.hpp 依赖的所有顶级库。 如果他们有包含守卫,他们将不会被重新包含。

   #include <iostream>
    #include <sstream>

    namespace AG {
    namespace Wrapper {
    #include "lib/AG/Wrapper/Valid.hpp"
    }
    }

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

    ...

但它不再是独立的。 :-(

更糟糕的是,有时纯标头库包含外部声明和其自身之外的内容的前向声明。 这些声明也被放置在命名空间内。 特别是,如果外部声明位于命名空间中定义的函数内部。

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

问:有更好的办法吗?

==:: 没有这样做==

提示::: 不这样做。至少不是一直如此,在 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。

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

以下不起作用

标头 整数变量; 程序文件 命名空间 Foo { 无效栏(){ 外部 int 变量; } }

尽管以下内容确实如此:

标头 整数变量; 程序文件 无效栏(){ 外部 int 变量; } }

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

即使你

== 反对将包含内容放入命名空间的意见 ==

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

E.g. 如何使用命名空间内单独标头中定义的类:

... 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 ...

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

如果您在命名空间中包含标头,则可能会出现以下一些问题。

  1. 该标头包含其他标头,这些标头也包含在命名空间内。然后另一个地方也想包含这些标头,但来自命名空间之外。由于标头具有包含保护,因此只有其中一个包含实际上生效,并且标头中定义的内容的实际名称空间突然微妙地取决于包含其他标头的顺序。

  2. 标头或其包含的任何标头均应位于全局命名空间中。例如,标准库头经常(为了避免冲突)引用其他标准内容(或实现细节):::std::other_stuff,即期望std直接位于全局命名空间中。如果您在命名空间中包含标头,则情况不再如此。该内容的名称查找将失败,并且标头将不再编译。这不仅仅是标准标头;我确信有一些这样的例子,例如也在 Boost 标头中。

  3. 如果您通过确保首先包含所有其他标头来解决第一个问题,并通过确保不使用完全限定名称来解决第二个问题,那么事情仍然可能会出错。有些图书馆要求其他图书馆专门化他们的东西。例如,图书馆可能想要专门化std::swap, std::hash or std::less对于它自己的类型。 (你可以超载std::swap相反,但你不能这样做std::hash and std::less。)执行此操作的方法是关闭特定于库的命名空间,打开命名空间std,并将专业化放在那里。除非库的标头包含在任意深度嵌套的命名空间中,否则它无法关闭这些命名空间。命名空间std它尝试打开不会::std, but ::YourStuff::std,它可能不包含任何专门化的主要模板,即使包含,这仍然是错误的做法。

  4. 最后,命名空间中的事物与外部事物的名称不同。如果您的库不是仅限标头的,而是具有已编译的部分,则已编译的部分可能不会将所有内容嵌套在命名空间中,因此库中的内容与您刚刚包含的内容具有不同的名称。换句话说,你的程序将无法链接。

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

但是您有一个不使用命名空间的旧库,并且您希望更新它以使用它们而不破坏所有旧代码。这就是你的should do:

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

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

#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”并获取命名空间版本。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

#includes 在命名空间中,将预先编写的内容“嵌入”命名空间中 的相关文章

  • C# SmtpClient编程中如何设置带有中文的附件文件名?

    我的代码如下 ContentType ct new ContentType ct MediaType MediaTypeNames Application Octet ct Name 这是一个很长的中文文件名希望能用它在附件名中 Doc A
  • 静态构造函数和 BeforeFieldInit?

    如果类型没有静态构造函数 则将执行字段初始值设定项 就在使用该类型之前 或者在某个时间点突发奇想 运行时 为什么这段代码 void Main start Dump Test EchoAndReturn Hello end Dump clas
  • C#.Net 邮件将进入垃圾邮件文件夹

    我正在从 ASP net Web 应用程序发送电子邮件 邮件发送成功 没有失败 但大多数都进入了垃圾邮件文件夹 请帮助我克服垃圾邮件过滤器 我的发送邮件代码 public void SendMail string FromAddress s
  • 捕获 .aspx 和 .ascx 页面中的异常

    问题说明了一切 请看以下示例代码 ul li li ul
  • 无法继承形状

    为什么我不能使用继承 a 的类Shapes class http msdn microsoft com en us library ms604615 28v vs 90 29 我需要延长Rectangle具有一些方法的类 但我想以与使用相同
  • strlen() 编译时优化

    前几天我发现你可以找到编译时strlen使用这样的东西 template
  • C# 中一次性对象克隆会导致内存泄漏吗?

    检查这个代码 class someclass IDisposable private Bitmap imageObject public void ImageCrop int X int Y int W int H imageObject
  • 混合模型优先和代码优先

    我们使用模型优先方法创建了一个 Web 应用程序 一名新开发人员进入该项目 并使用代码优先方法 使用数据库文件 创建了一个新的自定义模型 这 这是代码第一个数据库上下文 namespace WVITDB DAL public class D
  • Android NDK 代码中的 SIGILL

    我在市场上有一个 NDK 应用程序 并获得了有关以下内容的本机崩溃报告 SIGILL信号 我使用 Google Breakpad 生成本机崩溃报告 以下是详细信息 我的应用程序是为armeabi v7a with霓虹灯支持 它在 NVIDI
  • MVC 5 中具有 ASP.NET Identity 的 Autofac 不会验证 OWIN 管道中的安全标记

    我在 MVC 5 中设置了 AutoFac 来与 ASP NET Identity 一起使用 表面上一切似乎都工作正常 即用户可以创建帐户并登录 但后来我发现 当安全标记更改时 用户不会注销 通过在 AspNetUsers 表中进行暴力破解
  • C# 获取数据表中所有重复行的计数

    我通过运行存储过程来填充数据集 并且从数据集中填充数据表 DataSet RawDataSet DataAccessHelper RunProcedure storedprocedureName this will just return
  • 如何在 Javascript 中连接 C# ActiveX 事件处理程序

    我尝试使用几个代码片段将 ActiveX 对象与 Javascript 事件处理程序挂钩 我无法确定为什么事件处理程序没有被调用 带有项目的 Github 存储库 https github com JesseKPhillips Csharp
  • 读取依赖步行者输出

    I am having some problems using one of the Dlls in my application and I ran dependency walker on it i am not sure how to
  • .NET 和 Mono 之间的开发差异

    我正在研究 Mono 和 NET C 将来当项目开发时我们需要在 Linux 服务器上运行代码 此时我一直在研究 ASP NET MVC 和 Mono 我运行 Ubuntu 发行版 想要开发 Web 应用程序 其他一些开发人员使用 Wind
  • 使用 gcc 时在头文件中查找定义的好方法是什么?

    在使用 gcc 时 有人有推荐的方法在头文件中查找定义吗 使用 MSVC 时 我只需右键单击并选择 转到定义 这非常好 我使用过 netbeans gcc 它确实有代码帮助 包括到定义的超链接 所以这是一种选择 但是 我想知道是否有任何其他
  • 在哪里可以找到 Microsoft.Build.Utilities.v3.5

    如何获取 Microsoft Build Utilities v3 5 我正在使用 StyleCop 4 7 Stylecop dll 中的 StyleCop msbuild 任务似乎依赖于 Microsoft Build Utilitie
  • 如何编写一个接受 int 或 float 的 C 函数?

    我想用 C 语言创建一个扩展 Python 的函数 该函数可以接受 float 或 int 类型的输入 所以基本上 我想要f 5 and f 5 5 成为可接受的输入 我认为我不能使用if PyArg ParseTuple args i v
  • Streamwriter 覆盖 txt 文件中的文本

    有没有什么方法可以重新打开流写入器而不创建新的写入对象 因为此时 当调用 WriteOdd 时 streamwriter 正在覆盖在它之前调用的 WriteEven public void WriteEven StreamWriter wr
  • 如果将变量设置为等于新对象,旧对象会发生什么?

    假设我们有一个 X 类not有一个超载的operator 功能 class X int n X n 0 X int n n n int main X a 1 an object gets constructed here more code
  • 嵌入式linux编写AT命令

    我在向 GSM 模块写入 AT 命令时遇到问题 当我使用 minicom b 115200 D dev ttySP0 term vt100 时它工作完美 但我不知道如何在 C 代码中做同样的事情 我没有收到任何错误 但模块对命令没有反应 有

随机推荐