C++中是否保留了C标准库的符号?

33

这是对另一个问题的后续。

原问题存在其他问题,但我不得不意识到主要问题(根据CLang)是在仅使用好的C++包含文件的情况下将time重新定义为另一个符号。

因此,这里有一个简化版本:

#include<iostream>

using std::cout;
using std::endl;

class time
{
public:
    int h, min, sec;
};

const int full = 60;

void canonify(time& pre)     // Error here (line 14)
{
    pre.min += pre.sec / full;
    pre.h += pre.min / full;
    pre.sec %= full;
    pre.min %= full;
}
int main()
{
    time a;                  // and here (line 23)
    a.h = 3;
    a.min = 128;
    a.sec = 70;
    canonify(a);
    cout << a.h << ":" << a.min << ":" << a.sec << endl;
}
当然,将time替换为不同的符号或使用struct time就足以解决问题。换句话说,我的问题并不是如何使代码运行,而只是我们是否必须将来自C库的符号视为C ++中的保留标记。 Clang 11(在MSVC19上)会出现错误:
1>ess.cpp(14,15): error : must use 'class' tag to refer to type 'time' in this scope
1>...\ucrt\time.h(518,42): message : class 'time' is hidden by a non-type declaration of 'time' here
1>ess.cpp(23,5): error : must use 'class' tag to refer to type 'time' in this scope
1>...\ucrt\time.h(518,42): message : class 'time' is hidden by a non-type declaration of 'time' here

那么问题是:C++标准在何处禁止在编译单元中未明确包含的情况下自由使用来自C标准库的符号?


有趣的是,同样的代码(一旦被翻译...)在C中可以正常工作:

#include <stdio.h>

//
typedef struct 
{
    int h, min, sec;
}time;
//
const int full = 60;
//
void canonify(time* pre)
{
    pre->min += pre->sec / full;
    pre->h += pre->min / full;
    pre->sec %= full;
    pre->min %= full;
}
int main()
{
    time a;
    a.h = 3;
    a.min = 128;
    a.sec = 70;
    canonify(&a);
    printf("%d:%d:%d\n", a.h, a.min, a.sec);
    return 0;
}

2
这似乎是一个重复的问题,但我不确定是否可以使用我的“重复”工具。 - bolov
4
全局命名空间中标识符很杂乱。Titus Winters 称之为“荒野”。你的代码并不禁止使用来自 C 标准库的全局命名空间中的符号。包括 C++ 封装的 C 头文件并不必须在全局命名空间中提供 C 符号(但大多数都会这样做——大多数,但并非全部),无论如何它们都是“保留”的。 - Eljay
2
@bolov:确实是同样的问题。但是“语言律师”标签要求回答必须精确参考标准。不过我必须承认,如果我先看到了另一个问题,我就不会问这个问题了;-) - Serge Ballesta
1
FYI,std::endl 在这里完全是多余的。从 main 返回将刷新 std::cout。通常情况下,在打印 '\n' 时无需强制刷新 - 在 stdout 是 TTY 的情况下,cout 通常已经是行缓冲的,因此不需要额外操作。 - Peter Cordes
2
你的 C 代码之所以能够正常工作,是因为 C 标准禁止 C 标准库头文件(除了一小部分有文档说明的例外情况)相互包含(或表现得好像它们相互包含),而 C++ 标准允许 C++ 标准库头文件相互包含。因此,iostream 可以并且确实从 ctime 中公开符号,但是 stdio.h 在编译为 C 时可能不会从 time.h 中公开符号。 - zwol
@bolov:确实如此-两个问题都没有标记为C++标准,而这是需要精确标准参考的问题。特别是,所提出的重复问题将基于C++14,而我们现在已经有了C++20。 - MSalters
2个回答

40

[extern.names]

3 C标准库中使用外部链接声明的每个名称,都保留给实现使用作为具有“C”外部链接的名称,既在std命名空间中,也在全局命名空间中。

请注意,该段落保留了名称本身。 因此,在全局命名空间中别名time会违反此约定。


5
解决方法是在自己的命名空间中包装您自己的“时间”对象。 - Eljay
3
在此条件下,您不能为其使用C语言链接。 - StoryTeller - Unslander Monica
3
除了那个命名空间、main函数和跨语言接口之外,每次声明都应该这样做。 - eerorika
2
谢谢,我找不到那个参考资料。但是C标准库中的符号数量非常大,我认为许多程序员可能不会全部掌握... - Serge Ballesta
1
@SergeBallesta - 谨慎使用命名空间可以避免问题,正如先前提到的那样。至少在C++中你有后路。C语言也基本上是这样做的,但依赖于约定而不是语言机制来避免冲突。 - StoryTeller - Unslander Monica
显示剩余2条评论

18
C++标准在哪里禁止自由使用C标准库的符号?
最新草案:
[extern.names]: C标准库中每个具有外部链接性声明的名称都被保留给实现,以便用作在std命名空间和全局命名空间中具有extern "C"链接的名称。
C标准库中每个具有外部链接性声明的函数签名都被保留给实现,以便用作具有extern "C"和extern "C++"链接的函数签名或全局命名空间中的名称。
当它们未在编译单元中明确包含时呢?
如果您使用标准库,则标准库的所有名称保留都会生效。
如果您包括任何标准头文件(或者任何确切内容不受您控制,因此可能包含标准头文件的头文件),则您可能会间接包含其他标准头文件,包括从C继承的头文件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接