【问题标题】:When using C headers in C++, should we use functions from std:: or the global namespace?在 C++ 中使用 C 头文件时,我们应该使用 std:: 还是全局命名空间中的函数?
【发布时间】:2015-12-12 21:18:45
【问题描述】:

C 在某种程度上,不完全是 C++ 的一个子集。因此,我们可以通过稍微更改名称(stdio.hcstdiostdlib.hcstdlib)来使用 C++ 中的大多数 C 函数/头文件。

我的问题实际上是一种语义问题。在 C++ 代码中(使用最新版本的 GCC 编译器),我可以调用 printf("Hello world!");std::printf("Hello world!");,它们的工作原理完全相同。在我使用的参考文献中,它也显示为std::printf("Hello world!");

我的问题是,是否更喜欢在 C++ 中使用 std::printf();?有区别吗?

【问题讨论】:

  • 如果有一天他们强制将C 库符号转储到全局命名空间中是非法的,我更喜欢使用std:: 限定版本。 (另外我有点希望他们把它定为非法)。
  • @Galik:同意。这样可以避免使用 C++ 编译器解决很多关于 C 问题的愚蠢问题。
  • 没有“有点怀孕”。 C要么是子集,要么不是。事实是,它不是。这就是必须修改 C 头文件才能在 C++ 中工作的原因。
  • “几乎所有”在谈论一组不可数的许多元素时是一个非常无用的衡量标准。通过同样的论点,您可能会将 C 和 Java 联系起来。
  • @sasauke 不,它不是一个子集。 C 和 C++ 肯定共享一个子集,但 C 本身 不是 C++ 的一个子集。

标签: c++ language-lawyer std


【解决方案1】:

来自 C++11 标准(重点是我的):

D.5 C 标准库头文件 [depr.c.headers]

  1. 为了与 C 标准库兼容...
  2. 每个 C 标头都有一个 name.h 形式的名称,其行为就像每个名称都放在标准中一样 对应的 cname 标头的库命名空间被放置在 全局命名空间 范围内。 未指定这些名称是否首先在命名空间范围内声明或定义 (3.3.6) 命名空间 std 然后注入到通过显式使用声明 (7.3.3) 实现全局命名空间范围。
  3. 示例:标题<cstdlib>肯定在命名空间内提供了它的声明和定义 std。它还可以在全局命名空间中提供这些名称。标头<stdlib.h>肯定全局命名空间中提供了相同的声明和定义,这与C标准中的情况非常相似。它 也可以在命名空间 std 中提供这些名称。

不推荐使用 «name.h» 标头,它们已被确定为从未来修订中删除的候选对象。

因此,我建议包含 «cname» 标头并使用 std 命名空间中的声明和定义。

如果您出于某些原因必须使用 «name.h» 标头(已弃用,见上文),我建议您使用全局命名空间中的声明和定义。

换句话说:喜欢

#include <cstdio>

int main() {
    std::printf("Hello world\n");
}

结束

#include <stdio.h>

int main() {
    printf("Hello world\n");
}

【讨论】:

  • N3242 不是任何 C++ 标准。 N3337 与 C++11 差异最小的草案。
  • 另请参阅 Red hat 博客中 Jonathan Wakely 的 Why < cstdlib > is more complicated than you might think。他从 C++ 标准库实现者的角度详细说明了一些问题。他还提供了可以追溯到 C++98 的历史。
  • @sergej - 你会碰巧知道 C++03 对这个主题的处理吗?还是会发生什么?
  • 可能已被弃用,它们不可能很快被删除。恰恰相反,事实上。有一个建议删除已弃用的标签,请参阅 open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0619r0.html#3.5“最后,作为与 C 和 POSIX 的重要兼容层,C 标头将被永久保留,这一点似乎很清楚。可能值得不弃用标头,[..]”
  • @Sjoerd 有趣。更新提案:open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2139r1.html#3.9>
【解决方案2】:

&lt;cmeow&gt; 始终提供::std::purr,可能提供也可能不提供::purr

&lt;meow.h&gt; 始终提供::purr,可能提供也可能不提供::std::purr

使用保证由您包含的标题提供的表单。

【讨论】:

  • 伪装得很差的STL?
  • @nwp 不。 (15 个字符)
  • @T.C.不幸的是,当我在我的编译器上尝试时,&lt;cmeow&gt;&lt;meow.h&gt; 既没有提供::std::purr 也没有提供::purr,而是一个预处理器错误。只有&lt;cstdio&gt; 和/或&lt;stdio.h&gt; 提供::std::printf 和/或::printf。 :P
  • @L.F.您可能需要strcat 来生成::purr
【解决方案3】:

不,无论哪种方式都很好。

原始的意图是 &lt;___.h&gt; 标头将是 C 版本,将所有内容放在全局命名空间中,&lt;c___&gt; 标头将是 C++ 化版本,将所有内容在std 命名空间中。

不过,在实践中,C++ 版本将所有内容都放入了全局命名空间。并且没有明确的共识认为使用std:: 版本是“正确的做法”。

所以基本上,使用任何你喜欢的。最常见的可能是在全局命名空间中使用 C 标准库函数(printf 而不是 std::printf),但没有太多理由认为其中一个比另一个“更好”。

【讨论】:

  • “并且没有明确的共识认为使用 std:: 版本是“正确的做法”。嗯,是的,大家一致认为这是正确的做法。
  • 如何客观判断是否达成共识?
  • @JeremyFriesner 你在 SO 上发布了关于它的信息,看看你是否有不同意的 cmets。 :)
  • @DevSolar 然后在字典中查找“共识”这个词。这与标准所说的无关,而是 C++ 程序员所说的——尤其是他们做了什么。有一个原因,实际上每个标准库实现都提供了 C 头文件,并且让 C++ 头文件也将所有内容都放在了全局命名空间中。 :)
  • @DevSolar 仅供参考,最近 - 在您发表评论一年多后 - 该提案已提交委员会:open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0619r0.html#3.5 “最后,很明显 C 标头将基本上永远保留,作为与 C 和 POSIX 的重要兼容层。可能值得不弃用标头,[..]"
【解决方案4】:

唯一的区别是,在std::printf() 中,通过添加std:: 范围解析,您将保护自己免受将来有人编写具有相同名称的函数的影响,这会导致命名空间冲突。这两种用法都将导致完全相同的 OS API 调用(您可以在 Linux 下通过运行strace your_program 来检查它)。

我发现不太可能有人会命名这样的函数,因为printf() 是最常用的函数之一。此外,在 C++ 中,iostreams 优先于对 printf 等 cstdio 函数的调用。

【讨论】:

  • 相反,我觉得很有可能:printf 在 C++ 中由于缺乏强类型而被严重破坏,用更好的版本替换它是很自然的。
  • @KonradRudolph 如果你愿意,你可以这样找到它,但你错了;它并不意味着具有强类型,并且有许多问题无法通过所需的强类型轻松解决。这就是为什么许多类似的 C++ 解决方案比 printf 慢得多的原因。如果你想用“更好”的版本来替换它,你就是在破坏语言和程序员之间的契约,并且从一开始就处于罪恶状态。
  • @Alice 嗯,我没有违反任何合同:std::printfmynamespace::printf 不同,C++ 明确允许我定义自己的函数,其名称与 std 内部的函数相同.这根本没有争议。至于您声称printf 由于打字松散而有效,那当然也是错误的。 printf 甚至不是特别高效,还有许多更高效的强类型实现。
  • @KonradRudolph 绝对不正确;您违反了用标准编写的合同,即没有任何量词的 printf 明显适用于 C 构造。您使用命名空间(为全局命名空间起别名)不是一个好主意。这毫无争议
  • @Alice 你能引用这个标准吗?我不知道有任何这样的措辞。
【解决方案5】:

来自 C++11 标准:

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

因此,如果您使用&lt;cstdio&gt;,您可以确定printf 将在namespace std 中,因此不在全局命名空间中。
使用全局命名空间会产生名称冲突。 这不是 C++ 方式。

因此,我使用&lt;cstdio&gt; 标头并建议您这样做。

【讨论】:

  • 虽然我希望它以这种方式工作,但这不是真的。如果您包含&lt;cstdio&gt;,则可以保证 std::printf 将存在,但如果 ::printf 也将存在或不存在,则标准不保证。事实上,在我听说过的每个编译器中,当您包含 &lt;cstdio&gt; 时,将 ::printf is 注入到全局命名空间中。
【解决方案6】:

根据我自己的实践:使用std:: 前缀。否则有一天abs咬你一口,万一你使用浮点数。

不合格的abs 是指在某些平台上int 上定义的函数。在其他人它是超载的。但是,std::abs 对于所有类型总是重载。

【讨论】:

    【解决方案7】:

    只使用printf 而不使用std:: 可能会产生一些名称冲突,并且被许多c++ 开发人员认为是一种不好的做法。 Google 是您在这方面的朋友,但这里有一些链接,希望对您有所帮助

    Why is "using namespace std" considered bad practice? http://www.cplusplus.com/forum/beginner/61121/

    【讨论】:

    • using namespace std 是一种不好的做法,但在没有 std:: 限定符的情况下使用 printf 则不是。
    • using namespace std; 不是我的问题。我从不使用它。 printf();std::printf(); 在没有 using namespace std; 的情况下使用 C++ 工作,这就是我发布问题的原因。
    • @REACHUS 不同意。这两种情况没有区别。
    • 我永远不会使用std::printf,感觉很奇怪。
    • @KonradRudolph 我并没有说有什么不同,我只是表达了我的看法(更多理由参见我的回答)。
    【解决方案8】:

    在标准输出中

    这是标准 C 库头文件 @c stdio.h 的 C++ 版本, 它的内容(大部分)与该标题相同,但都是 包含在命名空间 @c std 中(定义的名称除外 作为 C 中的宏)。

    所以应该没什么区别。

    【讨论】:

      猜你喜欢
      • 2011-02-04
      • 2014-11-09
      • 1970-01-01
      • 1970-01-01
      • 2015-08-05
      • 1970-01-01
      • 2019-11-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多