标准是否允许在编译时比较指向不完整类型的extern const变量的指针?
以下代码被MSVC与 Clang接受,但在GCC(自13.2及其后续版本,包含trunk)中被拒绝,错误信息为 non-constant condition for static assertion.(https://godbolt.org/z/eneq3TWvs)
struct A;
extern const A a1;
extern const A a2;
static_assert(&a1 != &a2);
然而,如果 struct A 是一个完整对象(https://godbolt.org/z/dbKMKcnnP)
struct A;
struct A{};
extern const A a1;
extern const A a2;
static_assert(&a1 != &a2);
GCC(以及其他人)接受它。
直觉上,它在一个不完整对象上也应当工作,因为我们没有做任何需要编译器了解对象具体信息的事情,比如布局、成员和大小。
对标准做了一些研究后,我无法得出一个结论,认为GCC是错的。
此情形似乎不属于 [expr.const] 的那些并非被视为 core constant expression 的类别。
在C++20的标准论文(https://isocpp.org/files/papers/N4860.pdf)中,我看到的唯一相关情形是
一个表达式E 是核心常量表达式,除非按照抽象机器(6.9.1)的规则对E 进行求值时,会得到以下情况之一: ... (5.23) 当结果未指定的三路比较运算(7.6.8)、关系运算(7.6.9)或等价运算(7.6.10)运算符
我无法找到此类结果为何会被视为未指定的依据。直觉上,两个名称不同的 extern const 对象在编译期应该产生两个不同的重定位,随后由链接器处理。
在最新的C++标准草案论文(https://eel.is/c++draft/expr.const)中,增加了一个不属于 core constant expression 的新情形:
(2.28) 比较指针指向潜在的不可唯一的对象的等值运算符,如果两操作数的指针值与不同的求值相关联([basic.compound]),且它们都可能指向同一个潜在不可唯一对象中的同一偏移量,或者其中一个指向一个类型具有constexpr-unknown表示的对象;
其中
如果一个类型具有constexpr-unknown表示,其定义如下: (1.1) 是一个联合(union), (1.2) 是一个指针类型或成员指针类型, (1.3) 带有volatile限定, (1.4) 是拥有非静态数据成员且成员类型为引用类型的类类型,或 (1.5) 拥有基类或非静态成员,其类型具有constexpr-unknown表示。
被讨论的情形似乎不属于上述任一类别,因此在C++20/23/26中似乎没有对此的限制。
[basic.link] 似乎只对“两个声明是否指向同一对象”这一问题提供细节,而不对“两个extern const声明是否可能指向同一对象”作进一步说明。
[expr.eq] 也没有就这一有问题的情形提供有用的要点。
总之,问题在于:
- 在编译时比较指向incomplete type的 extern const变量的指针,是否符合C++的正确性?换言之,GCC的拒绝是否正确?
解决方案
这是一个 已确认的gcc缺陷,gcc的开发者表示:
Confirmed. 绝对不是回归性问题。GCC认为一个不完整类型在内部的大小可能是
0,并且a1和a2可能具有相同的地址。