【问题标题】:namespace issues in c++11?c++11中的命名空间问题?
【发布时间】:2012-10-13 22:58:37
【问题描述】:

谁能解释一下:

$ cat test.cpp 
#include <string>
std::string div;
$ g++ -c test.cpp 
$ g++ -std=c++11 -c test.cpp 
test.cpp:2:13: error: 'std::string div' redeclared as different kind of symbol
In file included from /usr/include/c++/4.7.1/cstdlib:66:0,
                 from /usr/include/c++/4.7.1/ext/string_conversions.h:37,
                 from /usr/include/c++/4.7.1/bits/basic_string.h:2814,
                 from /usr/include/c++/4.7.1/string:54,
                 from test.cpp:1:
/usr/include/stdlib.h:787:14: error: previous declaration of 'div_t div(int, int)'
$

对于 C++11 模式,div 符号是否也应该位于 std 命名空间中?还是我的系统特有的东西?

【问题讨论】:

  • 我发现编译器经常违反 C 标准库头文件的 c* 对应对象定义的所有内容都必须在 std 命名空间中的规则。显然string 包含cstdlib,这违反了规则并将div 放在全局命名空间中。我会称之为编译器缺陷。
  • 有趣的是它可以正常工作,直到我指定了 c++11 模式。我不确定它是否是编译器错误:clang 做了完全相同的事情......
  • @SethCarnegie - 库实现经常违反该规则的原因是没有这样的规则。在 C++03 中有,但这是一厢情愿的想法;它无法完成,并且在 C++11 中删除了该规则。
  • 是否有任何标准方法可以防止这种冲突,而不必在每次使用时限定我的 div 变量?
  • @PeteBecker 这令人失望。我知道 C++11 允许它,但我希望实现不这样做。我从来没有意识到 C++03 实际上不允许 c 头文件污染全局命名空间,而且实现根本不符合要求。

标签: c++ gcc c++11 glibc


【解决方案1】:

div 是来自&lt;stdlib.h&gt; 的函数。

在 C++11 中,&lt;cblah&gt; 标头允许在全局命名空间中放置内容。

C++11 §17.6.1.2/4
“除了第 18 条至第 30 条和附件 D 中的说明外,每个标题 c<i>name</i> 的内容应相同 与相应的标头 <i>name</i>.h 一样,如 C 标准库 (1.2) 或 C Unicode TR 中所指定的,视情况而定,如同通过包含一样。然而,在 C++ 标准库中,声明(除了 在 C 中定义为宏的名称在命名空间 std 的命名空间范围 (3.3.6) 内。它是 未指定这些名称是否首先在全局命名空间范围内声明然后注入 通过显式 using-declarations (7.3.3) 进入命名空间 std。”

反映常见 C++ 实现的现实。

所以实际上没有任何改变,除了正式的:您看到的行为现在已受到标准的认可,而不是必须考虑的实现工件。

此外,正式的更改更容易争论 SO 应该更好地包含 .h 标头而不是 cxxx 标头...

【讨论】:

    【解决方案2】:

    /usr/include/stdlib.h

    .h C stdlib 标头中的每个名称都驻留在全局命名空间中(显然)。

    此外,任何cHEADER C++ stdlib 标头都将在std 命名空间中定义来自HEADER.h 的相应名称,但也允许将它们放在全局命名空间中(因此它们可以这样做

    // cHEADER
    #include <HEADER.h>
    
    namespace std{
    using ::one_name_from_HEADER;
    using ::another_name_from_HEADER;
    // and so on...
    }
    

    并完成它)。

    §D.5 [depr.c.headers]

    p2 每个 C 标头都有一个 name.h 形式的名称,其行为就像每个由相应 cname 标头放置在标准库名称空间中的名称都放置在全局名称空间范围内一样。未指定这些名称是否首先在命名空间 std 的命名空间范围 (3.3.6) 内声明或定义,然后通过显式使用声明 (7.3.3) 注入全局命名空间范围。

    p3 [ 示例: 标题&lt;cstdlib&gt; 确实在命名空间std 中提供了它的声明和定义。它还可以在全局命名空间中提供这些名称。标题&lt;stdlib.h&gt; 确实在全局命名空间中提供了相同的声明和定义,就像在 C 标准中一样。它还可以在命名空间 std 中提供这些名称。 ——结束示例 ]

    如您所见,反过来也是如此(&lt;HEADER.h&gt; 可能会将名称引入std 命名空间,好像

    // HEADER.h
    #include <cHEADER>
    
    using std::one_name_from_HEADER;
    using std::another_name_from_HEADER;
    // and so on...
    }
    

    ),这使得这些标头之间的整体区别相当...无用,真的。

    【讨论】:

    • 好吧,不是没用;您得到的保证是#include &lt;stdio.h&gt; 将一堆名称放入全局命名空间,而#include &lt;cstdio&gt; 将一堆名称放入命名空间std
    • 另一个使用 cheaders 的好理由是避免使用宏。例如,&lt;cctype&gt; 通常会取消定义 &lt;ctype.h&gt; (reference) 中的所有宏。
    • @JesseGood:你知道 Visual C++(你的参考)在这方面是否符合标准吗?
    • @Cheersandhth.-Alf:AFAIK,它符合标准。 libstdc++ 做同样的事情。 C 标准说Any function declared in a header may be additionally implemented as a function-like macro。在 C++ 中,只有 Names which are defined as macros in C shall be defined as macros in the C++ standard library,因为 C 标准说 may be additionally implemented,所以在 C++ 中将它们设为宏没有任何限制。
    【解决方案3】:

    17.6.4.3.3/1 状态:

    在标头中声明为具有外部链接的对象的每个名称都保留给实现以指定具有外部链接的库对象,182在命名空间std和全局命名空间中。

    div 是在头文件 cstdlib 中声明为具有外部链接的函数的名称,因此是全局命名空间中的保留名称。你不能使用这个名字。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-03-04
      • 1970-01-01
      • 1970-01-01
      • 2011-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多