Bri*_*ian 7 c++ language-lawyer
考虑一个具有以下两个翻译单元的程序:
\n// TU 1\n#include <typeinfo>\nstruct S {\n enum { } x;\n};\nconst std::type_info& ti1 = typeid(decltype(S::x));\n\n\n// TU 2\n#include <iostream>\n#include <typeinfo>\nstruct S {\n enum { } x;\n};\nextern std::type_info& ti1;\nconst std::type_info& ti2 = typeid(decltype(S::x));\nint main() {\n std::cout << (ti1 == ti2) << \'\\n\';\n}\nRun Code Online (Sandbox Code Playgroud)\n我用 GCC 和 Clang 编译它,在这两种情况下结果都是 1,我不知道为什么。(GCC 还警告“ISO C++ 禁止空的未命名枚举”,我认为这不是真的。)
\n[dcl.枚举]/11指出,如果未命名枚举没有用于链接目的的 typedef 名称,但至少有一个枚举器,则它的第一个枚举器作为其名称用于链接目的。这些枚举没有枚举器,因此它们没有用于链接目的的名称。同一段还有以下注释,这似乎是未出于链接目的给出枚举名称的自然结果:
\n\n\n[注 3:每个没有枚举器的未命名枚举都是不同的类型。\xe2\x80\x94 尾注]
\n
也许两个编译器都有一个错误。或者,更可能的是,我只是误解了这张纸条。无论如何,该注释是非规范性的,所以让我们看一些规范性的措辞。
\n\n\n\n\n两个实体声明声明同一实体,如果考虑未命名类型的声明以引入其名称以用于链接目的(如果有的话)([dcl.typedef],[dcl.enum]),它们对应([basic.scope.scope]) ,具有相同的目标范围,但不是函数或模板参数范围,并且
\n\n
\n- [无关]
\n- [无关]
\n- 他们都声明具有外部链接的名称。
\n
\n\n如果两个声明(重新)引入相同的名称、都声明构造函数或都声明析构函数,则它们对应,除非[不相关]
\n
看来,当未命名的枚举没有出于链接目的而给出 typedef 名称,并且没有枚举器时,它在不同的翻译单元中不能与自身具有相同的类型。
\n那么这真的只是一个编译器错误吗?我想到的最后一件事是,如果两个枚举类型确实不同,则 的多个定义S违反了单一定义规则,并使程序形成格式错误的 NDR。但我在 ODR 中找不到任何实际说明这一点的内容。
如所见,该程序格式良好并打印 1。因为S在具有外部链接的两个翻译单元中定义相同,所以就好像 ([basic.def.odr]/14) 有一个定义S,因此只定义了一种枚举类型。(实际上,它是根据名称S或被破坏的S::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;
Run Code Online (Sandbox Code Playgroud)
// bar.cc
#include"foo.hh"
void init() {v.push_back(g(f()));}
Run Code Online (Sandbox Code Playgroud)
// main.cc
#include"foo.hh"
void init();
int main() {
init();
return v.front()()!=f(); // 0
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
457 次 |
| 最近记录: |