Bel*_*loc 5 c++ language-lawyer constexpr c++17
二者clang并g++似乎是符合在C++标准的段落[expr.const]/5的最后一个版本.以下代码段为两个编译器打印11.查看实例:
#include <iostream>
void f(void) {
static int n = 11;
static int* temp = &n;
static constexpr int *&&r = std::move(temp);
std::cout << *r << '\n';
}
int main()
{
f();
}
Run Code Online (Sandbox Code Playgroud)
根据我对本段的理解,两个编译器都应该打印2016下面的代码.但他们没有.因此,我必须得出结论,代码显示未定义的行为,因为clang打印任意数字并g++打印0.我想知道为什么它是UB,考虑到例如标准的N4527草案?实例.
#include <iostream>
void f(void) {
static int n = 11;
static int m = 2016;
static int* temp = &n + 1;
static constexpr int *&&r = std::move(temp);
std::cout << *r << '\n';
}
int main()
{
f();
}
Run Code Online (Sandbox Code Playgroud)
编辑
我习惯不满足于只是说代码是UB的答案,或者显示未定义的行为.我总是喜欢进行更深入的调查,有时候,就像现在一样,我很幸运能够更多地了解编译器是如何构建的.这就是我在这种情况下发现的:
双方clang并GCC似乎以消除任何未使用的变量,比如m,从代码,对任何优化级别大于-O0.GCC似乎用静态存储持续时间对局部变量进行排序,变量放在堆栈上的方式相同,即从高地址到低地址.
因此,clang如果我们更改优化级别,-O0我们2016将按预期打印数字.
在GCC,除此之外,我们还改变了定义
static int* temp = &n + 1;
Run Code Online (Sandbox Code Playgroud)
至
static int* temp = &n - 1;
Run Code Online (Sandbox Code Playgroud)
我们还将获得2016代码打印的数字.
我认为这里没有任何微妙之处.&n + 1你可以考虑位置的一个数组的一个接一个点n,因此它不构成可解除引用的指针,尽管它是一个完全有效的指针.因此,temp并且r是完美的constexpr变量.
你可以r像这样使用:
for (int * p = &n; p != r; ++p) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
这个循环甚至可以出现在constexpr函数中.
当您尝试取消引用时r,行为当然是未定义的,但这与常量表达式无关.
| 归档时间: |
|
| 查看次数: |
147 次 |
| 最近记录: |