【问题标题】:Are C keywords/functions not enclosed in std namespace in C++?C 关键字/函数是否未包含在 C++ 的 std 命名空间中?
【发布时间】:2020-07-19 06:00:20
【问题描述】:

以下代码在我的本地系统上运行良好,但在在线平台上引发编译错误。

#include <iostream>

int32_t time[int32_t(1e5)];

int main()
{
    int32_t n;
    std::cin>>n;
    for(int32_t i=0;i<n;++i)
    {
        int32_t temp;
        std::cin>>temp;
        --temp;
        ++time[temp];
    }
    
    return 0;
}

很明显,错误是因为第3行int32_t time[int32_t(1e5)];而产生的 但是,我无法理解为什么会发生编译错误。

我没有包含ctime 或任何此类标头,甚至没有打开std 命名空间。但是还是发生了编译失败。

我的系统上有 gcc 8.3.0 和 __cplusplus 201703。不知道在线平台上的版本是什么。

【问题讨论】:

  • 错误信息告诉你标题包含链 - 从iostream开始。
  • 这就是用户 C 库为其名称添加前缀的原因。您应该避免使用标准在全局范围内使用的名称。
  • 尽管包括ctime,但您也没有包括int32_t 的正确标题(例如cstdint)。
  • 我认为允许实现将 C 库定义放置在全局命名空间以及 std 命名空间中。
  • @deadLock en.cppreference.com/w/cpp/header 请参阅“C 兼容性标头”部分

标签: c++ std backwards-compatibility


【解决方案1】:

标准规定,当您包含任何标准包含文件时,它可能会包含其他包含文件。这取决于具体的实现方式和数量。

这意味着您的代码根本不能定义任何也是标准的全局名称。

我可以理解这似乎是一个困难的要求(确实如此),这也让人想知道为什么有标准的包含文件,而我们没有简单的整个标准可用(这是一个好问题)。但无论如何,情况就是这样。

POSIX 的情况更糟,不仅保留了随机名称,而且还有很多前缀后缀;例如,使用任何名称 startingLC_ 后跟大写字母的代码可能与与语言环境支持相关的 #defines 发生冲突。任何以_t 结尾的名字也是保留的,不是开玩笑的。 The list is huge.

作为一般规则,尽量定义尽可能少的全局名称,并避免使用标准库也使用的任何名称。即使在您的编译器上“它可以工作”,您的程序在移植到另一个编译器(或同一编译器的下一个版本)时也可能会发现问题。避免定义全局名称还可以使您的代码更容易与其他人编写的代码集成到更大的程序中。理想情况下,您的代码应该只有一个全局名称(命名空间、单个类或单个函数)......不幸的是,对于 C++,您无法低于该名称。

当我通常不关心这些名称冲突问题时,我记得在编写小型 C++ 实验时碰到的东西是例如y0,它是一个标准 Bessel 函数(这不是开玩笑;有一个全局标准函数double y0(double) 和任何在全局级别使用 y0 的程序都不是有效的 C++ 程序。

【讨论】:

  • 我用“时间”作为局部变量运行相同的代码,它运行良好。 如果我在本地空间中声明变量,我能否保证不会出现名称冲突的问题? 因为,我肯定永远无法记住所有保留的名称。
  • @deadLock:保留为潜在宏的名称要少得多,因此对于本地名称,情况要好得多。事实上,我认为没有人能记住大量的保留名称前缀和后缀......这就是为什么一般规则是尽可能避免使用全局命名空间。
【解决方案2】:

您间接包含了头文件time.h。在这个头文件中有一个名为 time 的函数的声明,它与您的声明冲突。

只需将变量 time 更改为另一个名称 (time_1)。

【讨论】:

  • &lt;time.h&gt; 中的time 实际上是一个声明的函数,而不是一个变量。
  • @Peter 因此错误消息显示time 被重新声明为不同类型的符号。
  • @RemyLebeau - 是的,我知道这一点。然而,当我输入之前的评论时,我并没有简单地重申显而易见的事实。当时 - 在随后的编辑之前 - 这个答案直接表明标准头 &lt;time.h&gt; 声明了一个名为 time 的变量。
  • @Peter 是的,它是一个函数声明。我的意思是有一个相同的名称冲突。
【解决方案3】:

在 C++ 中,C 关键字/函数是否未包含在 std 命名空间中?

关键字(还有宏):不,它们不在命名空间中。

函数、类型和变量(即除宏之外的所有标识符):取决于您包含的标准头。

如果您包含 C 标准标头,例如 &lt;stdint.h&gt;,则名称将位于全局命名空间中。它们也可能在 std 命名空间中,但这不能保证。

如果包含相应的 &lt;cstdint&gt; 标头,则 C 标准标头中的名称保证在 std 命名空间中。它们也可能在全局命名空间中,但不能保证。

您未能包含&lt;stdint.h&gt;&lt;cstdint&gt;,因此无法保证int32_t 会在任一命名空间中声明。但是您已经包含了另一个标准标头,因此不能保证它不会在某些命名空间中声明 - 因为标准标头可能包含其他标头;除非在标准中记录,否则您永远不应该依赖这种传递包含(以您的示例依赖它的方式)。

同样适用于time 函数。您已经包含了一个标准头文件,但不能保证它不会包含另一个声明 time 的标准头文件。并且不能保证它不会在全局命名空间中。

无论您是否包含任何标准头文件,C 标准库使用的所有名称都保留给全局命名空间中的语言实现。通过自己定义::time,你的程序的行为将是不确定的(允许UB编译失败,这是最好的结果)。

如果我在本地空间中声明变量,我可以保证不会出现名称冲突的问题吗?

如果是time,是的。 C 标准名称(当然宏除外)仅保留在全局命名空间中。本地名称不在全局命名空间中;他们会影响全球的,这很好。在您自己的自定义命名空间中定义这些也很好。

宏名称以及某些标识符(例如包含双下划线的标识符)在所有命名空间中都保留。所有的宏名称都是大写的,所以很容易通过在名称中包含小写字符来避免它们。


为避免名称与标准名称以及第三方库发生冲突,应仅在全局命名空间中声明一个名称(main 除外):一个包含所有其他命名空间范围声明的(希望是唯一的)命名空间。并且应尽可能避免使用宏,但在必要时,它们应包含一些(希望是唯一的)前缀。

【讨论】:

    猜你喜欢
    • 2019-11-15
    • 1970-01-01
    • 1970-01-01
    • 2022-07-21
    • 2014-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    相关资源
    最近更新 更多