【问题标题】:Identity of unnamed enums with no enumerators没有枚举数的未命名枚举的身份
【发布时间】: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:每个没有枚举数的未命名枚举都是不同的类型。 ——尾注]

也许两个编译器都有错误。或者,更有可能的是,我只是误解了这张纸条。无论如何,该注释是非规范性的,所以让我们看一些规范性措辞。

[basic.link]/8

如果考虑到未命名类型的声明以引入它们的名称以用于链接目的,则两个实体声明声明相同的实体,如果有([dcl.typedef],[dcl.enum]),它们对应([basic.scope. scope]),具有相同的目标范围,不是函数或模板参数范围,并且

  • [无关]
  • [无关]
  • 它们都使用外部链接声明名称。

[basic.scope.scope]/4

如果两个声明(重新)引入相同的名称、都声明构造函数或都声明析构函数,则它们对应,除非 [不相关]

似乎,当一个未命名的枚举没有为链接目的赋予 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


【解决方案1】:

这个程序是格式良好的,并且打印出 1,如图所示。因为S 在两个具有外部链接的翻译单元中定义相同,就好像S ([basic.def.odr]/14) 有一个 定义,因此只有一个枚举类型已定义。 (实际上,它是根据名称 SS::x 进行修改的。)

这与静态局部变量和 lambda 在 内联 函数的定义之间共享的现象相同:

// foo.hh
inline int* f() {static int x; return &x;}
inline auto g(int *p) {return [p] {return p;};}
inline std::vector<decltype(g(nullptr))> v;
// bar.cc
#include"foo.hh"
void init() {v.push_back(g(f()));}
// main.cc
#include"foo.hh"
void init();
int main() {
  init();
  return v.front()()!=f();  // 0
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多