是否可以在命名空间中放置标准的纯C头#include指令?

时间:2012-09-07 19:33:56

标签: c++ namespaces include

  

可能重复:
  Is it a good idea to wrap an #include in a namespace block?

我在全局命名空间(log)中有一个类::log的项目。

因此,自然地,在#include <cmath>之后,每当我尝试实例化我的日志类的对象时,编译器都会给出错误消息,因为<cmath>使用许多三字母方法污染全局命名空间,其中一个是对数函数log()

因此,有三种可能的解决方案,每种解决方案都有其独特的丑陋副作用。

  • 将日志类移动到它自己的命名空间,并始终使用它的完全限定名称访问它。我真的想避免这种情况,因为记录器应尽可能方便使用。
  • 编写mathwrapper.cpp文件,该文件是项目中唯一包含<cmath>的文件,并通过<cmath>中的包装器提供所有必需的namespace math函数。我不想使用这种方法,因为我必须为每个必需的数学函数编写一个包装器,它会增加额外的调用惩罚(部分由-flto编译器标志取消)
  • 我正在考虑的解决方案:

替换

#include <cmath>

通过

namespace math {
#include "math.h"
}

然后通过math::log()计算对数函数。

我已经尝试过,确实可以按预期编译,链接和运行。但它有多重缺点:

所以我的问题是:

  • 是否有任何推荐/约定/等禁止在命名空间中包含include指令?
  • 可能出现问题
    • 不同的C标准库实现(我使用glibc),
    • 不同的编译器(我使用g ++ 4.7,-std = c ++ 11),
    • 联?
  • 你有没有试过这样做?
  • 有没有其他方法可以从全局命名空间中消除数学函数?

我在stackoverflow上发现了几个类似的问题,但大多数是关于包含其他C ++头文件,这显然是一个坏主意,而那些没有关于C库链接行为的矛盾陈述。另外,将#include <math.h>添加到extern "C" {}

中也是有益的

修改

所以我决定做其他人正在做的事情,并将我的所有代码放在项目名称空间中,并在包含<cmath>时使用它的完全限定名称访问记录器。

4 个答案:

答案 0 :(得分:18)

不,您正在考虑的解决方案是不允许的。实际上,这意味着您正在更改头文件的含义。您正在更改其所有声明以声明不同命名的函数。

这些更改的声明将与标准库函数的实际名称不匹配,因此,在链接时,任何标准库函数都不会解析对已更改声明声明的函数的调用,除非它们恰好已声明{{ 1}}允许 - 但不推荐 - 来自C标准库的名称。

ISO / IEC 14882:2011 17.6.2.2/3 [using.headers]适用于C标准库头文件,因为它们是C ++标准库的一部分:

  

翻译单位应仅在任何外部声明或定义[*]之外包含标题,并且应在该翻译单元中的第一次引用之前以词汇方式包含该标题。该文件中声明的任何实体。

[*]包含命名空间定义。

答案 1 :(得分:6)

为什么不将日志类放在它自己的命名空间中并使用typedef namespace::log logger;以更方便的方式避免名称冲突?

答案 2 :(得分:2)

Change your class's name. Not that big of a deal.; - )

严重的是,将名称放在全局命名空间中与任何标准标题中的名称冲突并不是一个好主意。 C ++ 03没有明确允许<cmath>定义::log。但由于在现有<cmath>之上定义<math.h>的实用性(以及某些标头的现有静态链接库,包括数学),实现长期不符合这一点。所以C ++ 11批准了现有的做法,并允许<cmath>将所有内容转储到全局命名空间中。 C ++ 11还保留所有这些名称以用于extern“C”链接,以及所有用于C ++链接的函数签名,即使您不包含标题。但稍后会更多。

因为在C ++中,任何标准头都允许从任何其他标准头定义名称(即,它们允许彼此包含),这意味着任何标准头都可以定义::log。所以不要使用它。

关于不同实现的问题的答案是,即使您的方案开始工作(不保证),在某些其他实现中,您可能会使用(或者希望将来在与您的日志类相同的TU),包括<cmath>,并且您没有给namespace math处理。在我看来,<random>似乎是我的候选人。它提供了一大堆连续的随机数分布,这些分布似乎可以与数学函数一起实现。

我建议Log,但后来我喜欢大写的类名。部分原因是它们总是与标准类型和功能不同。

另一种可能性是像以前一样定义您的课程,并使用struct log代替log。这不会与函数冲突,原因只有在您花费太多时间使用C和C ++标准时才会变得清晰(您只使用log作为名称,而不是作为一个函数,而不是一个带有“C”链接的名称,所以你不要侵犯保留名称。尽管有相反的表现,C ++中的类名仍然存在于其他名称的并行世界中,而不像{{1标签在C)中完成。

不幸的是struct不是简单类型标识符,因此例如您无法使用struct log创建临时符号。要定义简单类型标识符:

struct log(VERY_VERBOSE, TO_FILE)

我在下面的评论中根据陈述的示例用法说明的一个例子。我认为这是有效的,但我不确定:

typedef struct log Log;
Log(VERY_VERBOSE, TO_FILE); // unused temporary object

答案 3 :(得分:2)

它也是丑陋的黑客,但我相信不会导致任何链接器问题。只需从<math.h>

重新定义日志名称即可
 #define log math_log
 #include <math.h>
 #undef log

使用此日志可能会导致数学内联函数出现问题,但也许你很幸运......

Math log()仍然可以访问,但这并不容易。在您想要使用它的函数中,只需重复其真实声明:

    int somefunc() {
        double log(double); // not sure if correct
        return log(1.1);
    }