【发布时间】:2021-10-25 21:03:24
【问题描述】:
考虑一个具有以下两个翻译单元的程序:
// TU 1
#include <typeinfo>
struct S {
enum { } x;
};
const std::type_info& ti1 = typeid(decltype(S::x));
// TU 2
#include <iostream>
#include <typeinfo>
struct S {
enum { } x;
};
extern std::type_info& ti1;
const std::type_info& ti2 = typeid(decltype(S::x));
int main() {
std::cout << (ti1 == ti2) << '\n';
}
我用 GCC 和 Clang 编译它,在这两种情况下结果都是 1,我不知道为什么。 (GCC 还警告说“ISO C++ 禁止空的未命名枚举”,我认为这不是真的。)
[dcl.enum]/11 指出,如果一个未命名的枚举没有用于链接目的的 typedef 名称但至少有一个枚举数,那么它将其第一个枚举数作为其名称以用于链接目的。这些枚举没有枚举数,因此它们没有用于链接目的的名称。同一段还有以下注释,这似乎是不为链接目的提供枚举名称的自然结果:
[注 3:每个没有枚举数的未命名枚举都是不同的类型。 ——尾注]
也许两个编译器都有错误。或者,更有可能的是,我只是误解了这张纸条。无论如何,该注释是非规范性的,所以让我们看一些规范性措辞。
如果考虑到未命名类型的声明以引入它们的名称以用于链接目的,则两个实体声明声明相同的实体,如果有([dcl.typedef],[dcl.enum]),它们对应([basic.scope. scope]),具有相同的目标范围,不是函数或模板参数范围,并且
- [无关]
- [无关]
- 它们都使用外部链接声明名称。
如果两个声明(重新)引入相同的名称、都声明构造函数或都声明析构函数,则它们对应,除非 [不相关]
似乎,当一个未命名的枚举没有为链接目的赋予 typedef 名称,并且没有枚举器时,它在不同的翻译单元中不能与它自身的类型相同。
那么它真的只是一个编译器错误吗?我在想的最后一件事是,如果这两种枚举类型确实不同,那么S 的多个定义违反了单一定义规则并使程序格式错误的 NDR。但我在 ODR 中找不到任何真正这样说的内容。
【问题讨论】:
-
有趣的是,在一个 TU 的全局范围内拥有
enum {} x;,在另一个 TU 中拥有enum {} y;在 Clang 中给出相同的 typeid,但在 GCC 中却不同。 -
如果它们是不同的类型,
S是否违反 ODR? -
他们都用外部链接声明名称 [basic.link]/8 也无关
-
... 如果
S违反了 ODR,那么您就有了一个格式错误的 ndr 程序。所以所有编译器都是正确的。 -
@Yakk-AdamNevraumont 如果您能找到使其违反 ODR 的特定段落,请发布答案。就个人而言,如果标准打算禁止这样做,我会感到惊讶。如果这种违反直觉的行为真的是有意为之,我至少会收到编译器警告。
标签: c++ language-lawyer