没有枚举数的未命名枚举的标识

Identity of unnamed enums with no enumerators

考虑一个包含以下两个翻译单元的程序:

// 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 名称但至少有一个枚举器,则它有第一个枚举器作为其用于链接目的的名称。这些枚举没有枚举器,因此它们没有用于链接目的的名称。同一段落还有以下注释,这似乎是不为链接目的提供枚举名称的自然结果:

[Note 3: Each unnamed enumeration with no enumerators is a distinct type. — end note]

也许两个编译器都有错误。或者,更有可能的是,我只是误解了这张纸条。反正这个note是非规范的,我们来看一些规范的写法。

[basic.link]/8

Two declarations of entities declare the same entity if, considering declarations of unnamed types to introduce their names for linkage purposes, if any ([dcl.typedef], [dcl.enum]), they correspond ([basic.scope.scope]), have the same target scope that is not a function or template parameter scope, and

  • [irrelevant]
  • [irrelevant]
  • they both declare names with external linkage.

[basic.scope.scope]/4

Two declarations correspond if they (re)introduce the same name, both declare constructors, or both declare destructors, unless [irrelevant]

看起来,当一个未命名的枚举没有为链接目的指定一个 typedef 名称,并且没有枚举器时,它不能在不同的翻译单元中与它本身是相同的类型。

那么它真的只是一个编译器错误吗?我在想的最后一件事是,如果这两个枚举类型确实不同,那么 S 的多个定义违反了一个定义规则并使程序格式错误的 NDR。但我在 ODR 中找不到任何实际说明的内容。

如所见,此程序格式正确并打印 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
}