关于constexpr
的语义,有一些我不明白的地方。当然,在堆栈上分配的x
的地址不能在编译时计算。
constexpr auto foo()
{
int x = 5;
return &x;
}
static_assert(foo() == 0);
字符串
尽管如此,这段代码编译(GCC 13.2 -std=c++20
),x
的地址等于0
,这可以在通过的static_assert
声明中看到。
请参阅https://godbolt.org/z/Pjrx3cb68。
3条答案
按热度按时间plupiseo1#
指向对象的指针可以在常量表达式中以有限的方式使用,即使这些对象从未存在于常量表达式中,或者已经死亡:
字符串
在表达式
foo() == 0
中,0
实际上是一个空指针常量,所以你所做的等效于&x == nullptr
,尽管是间接的。这个比较的结果具有实现定义的效果,因为x
是死的,而foo()
产生一个 * 无效的指针值 *:当达到存储区域的持续时间的结束时,表示该存储区域的任何部分的地址的所有指针的值变为无效指针值。通过无效指针值的间接访问和将无效指针值传递给释放函数具有未定义的行为。任何其他无效指针值的使用都有实现定义的行为。
Assert:
型
clang失败,GCC 14之后也失败。但是,GCC 13及更低版本将此无效指针值视为等于null,因此比较成功。请参阅live example at compiler explorer。
4c8rllxm2#
在常量表达式的求值中,如果变量的生存期仅限于常量表达式的求值,则可以像在任何其他表达式求值中一样正常使用变量。(即编译器将在编译时模拟一个堆栈,用于计算常量表达式。
static_cast
需要它的 * 整个操作数 *,即foo() == 0
是一个 * 常量表达式 *。在计算
foo() == 0
时,您正在比较指针值,但指针值不是常量表达式结果的一部分,因此编译时指针转义到运行时上下文(常量表达式中不允许)没有问题。所以没有问题。但是,一旦
foo()
返回,返回的指针值就会变成 * 无效指针值 *,因为变量x
的存储时间结束。除了少数例外,任何无效指针值的使用都是实现定义的。与0
相比福尔斯这种实现定义的范畴。因此
static_assert(foo() == 0, "???");
是否编译是由实现定义的。最初,我预计编译器通常会将foo() == 0
视为false,因为如果假设成为无效指针值的指针保留其地址,则它不可能等于空指针值。然而,现在我似乎不清楚一些编译器供应商实际上期望这里的行为是什么。据我所知,他们缺乏这种实现定义的行为的文档。tvokkenx3#
这是一个已知的GCC bug:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110619,出现在GCC 13中,但在GCC Backbone.js 中已经修复,其中
static_assert(foo() != 0)
为true(悬空指针不等于nullptr
)。在线演示:https://gcc.godbolt.org/z/5bbP16h5o的请注意,您的程序的小修改:
字符串
(see
p
引入并返回的局部变量)也让GCC 13感到满意:https://gcc.godbolt.org/z/Wx1dMzT3K的