C++类成员指针:p2>p1合法时p2-p1是否有效?
C++中类成员指针编译期减法的编译器行为辨析
类的非静态成员指针可以进行比较,结果由成员的声明顺序决定。比如下面的代码是合法的,执行后返回true(即1):
struct A { int a0 = 1; int a1 = 2; }; consteval int foo() { A a; int* p1 = &a.a0; int* p2 = &a.a1; return p2 > p1; } int main() { return foo(); }
基于这个特性,很多人会认为p2 - p1应该返回两个int成员之间的对象间距(即1),这在运行时的代码中确实能得到预期结果:
struct A { int a0 = 1; int a1 = 2; }; int foo() { A a; int* p1 = &a.a0; int* p2 = &a.a1; return p2 - p1; } int main() { return foo(); }
但在编译期(比如使用consteval限定函数),三大主流编译器的表现却截然不同:
- GCC编译通过,返回预期的1;
- Clang直接提示未定义行为,对应的提示信息为:
subtracted pointers are not elements of the same array
- MSVC编译通过,但返回的是字节间距4,而非预期的对象间距1。
哪种编译器的行为是正确的?
根据C++标准的定义,指针减法操作只有在两个指针指向**同一数组对象的元素(或数组末尾的下一个位置)**时,才是定义良好的。类的非静态成员尽管在内存布局中可能连续排列(无填充的情况下),但每个成员都是独立的对象,并不属于同一个数组。
运行时编译器能得到预期结果,只是利用了内存布局的连续性做优化,这属于未定义行为中“表现符合预期”的巧合,标准并不保证这种行为的一致性。
回到编译期的表现:
- Clang的行为严格符合标准:它在编译期就检测到违反指针减法的规则,直接报告未定义行为,这是标准要求的正确处理方式。
- GCC的行为属于对未定义行为的“宽容处理”,虽然得到了预期结果,但这是编译器的非标准优化,并不符合标准规范。
- MSVC返回字节间距的行为,同样是基于自身内存布局的实现逻辑,但也不符合标准中指针减法的定义,属于未定义行为的一种表现。
简言之,只有Clang的行为严格遵循了C++标准,GCC和MSVC的行为都是未定义行为的不同实现表现。
内容的提问来源于stack exchange,提问作者doug
相关产品推荐
相关产品推荐

