C++局部变量生命周期结束后其指针编译时比较的合法性问题
问题描述
从C++17开始,我们可以定义一个constexpr函数,返回其内部局部变量的指针,这样调用方就会得到一个指向已结束生命周期对象的指针。
显然,对这类指针解引用会触发未定义行为,是被禁止的,但对这类指针做相等比较是否合法呢?
参考以下示例代码:
constexpr auto f() { char c = 0; auto p = &c; return p; }; int main() { static_assert( ( f() == f() ) == ( f() == f() ) ); //所有编译器均通过 static_assert( f() == f() ); //GCC判定为true,Clang判定为false }
第一个static_assert被所有编译器接受,即使存在未定义行为也没有任何警告,它的作用是验证f() == f()的结果在同一编译器下是一致的,要么始终为true要么始终为false。
GCC认为f() == f()的结果为true,而Clang则判定为false,后者的结果看起来更符合编译期逻辑。
请问上述两种编译器的实现哪一个是符合C++标准的?
解答
Clang的实现符合C++标准,GCC的处理不符合标准要求,原因如下:
- 首先明确
f()的行为:每次调用f()时,函数内部都会创建一个独立的局部char对象c,返回的指针指向该临时对象,调用结束后对象生命周期结束,因此返回值属于悬空指针。 - 运行期下对悬空指针做任意操作(包括相等比较)都属于未定义行为,但编译期常量求值有额外的语义约束:C++标准要求编译器在常量求值过程中必须追踪指针的来源标识,而不是仅比对地址的数值。
- 根据C++标准
[expr.eq]章节的规定,两个指针相等当且仅当它们指向同一个完整对象、同一个函数,或者同为空指针。每次调用f()生成的局部对象c是完全独立的不同实例,哪怕它们的存储地址数值相同,它们的指针来源标识也不同,因此f() == f()的求值结果应为false。 - 示例中第一个
static_assert全部编译器通过的原因是,它仅验证同一个表达式的求值结果和自身相等,无论f() == f()本身的结果是true还是false,这个断言恒成立。
内容的提问来源于stack exchange,提问作者Fedor
相关产品推荐
相关产品推荐

