c++ - 为什么我下面的第二个片段显示未定义的行为?

标签 c++ language-lawyer constexpr c++17

clangg++ 似乎都符合 C++ 标准中段落 [expr.const]/5 的最新版本。以下代码段为两个编译器打印 11。参见 live example :

#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();
}

根据我对这一段的理解,两个编译器都应该为下面的代码打印 2016。但他们没有。因此,我必须得出结论,该代码显示了未定义的行为,因为 clang 打印了一个任意数字,而 g++ 打印了 0。我想知道为什么是 UB,例如,考虑到标准的 N4527 草案? Live example .

#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();
}

编辑

我习惯于不满足于只说代码是 UB 或显示未定义行为的答案。我总是喜欢更深入地研究,有时,就像现在一样,我碰巧足够幸运地多了解一点编译器是如何构建的。这就是我在这种情况下发现的:

对于任何大于 的优化级别,clangGCC 似乎都从代码中消除了任何未使用的变量,例如>-O0GCC 似乎对具有静态存储持续时间的局部变量进行排序,这与变量放置在堆栈上的方式相同,即从高地址到低地址。

因此,在 clang 中,如果我们将优化级别更改为 -O0,我们将按预期打印出数字 2016

GCC中,如果除此之外,我们还更改了

的定义
static int* temp = &n + 1;

static int* temp = &n - 1;

我们还将得到代码打印的数字2016

最佳答案

我认为这里没有任何微妙之处。 &n + 1 指向一个数组的最后一个,您可以将其视为位置 n,因此它不构成可取消引用的指针,尽管它是一个完全有效的指针。因此 tempr 是非常好的 constexpr 变量。

你可以像这样使用 r:

for (int * p = &n; p != r; ++p) { /* ... */ }

这个循环甚至可以出现在 constexpr 函数中。

当您尝试取消引用 r 时,行为当然是未定义的,但这与常量表达式无关。

关于c++ - 为什么我下面的第二个片段显示未定义的行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34556422/

相关文章:

c++ - 组合框内的 QIcon

c++ - 两个 std::unique_ptr 指向同一个对象——未定义的行为?

c++ - constexpr new 如何分配内存?

c++ - constexpr 重载

C++ 使用 constexpr 使放置新对齐的存储可初始化

c++ - 如何在opencv中找到单个图像关键点之间的欧氏距离

c++ - 一个大世界还是许多小细胞?

c++ - GCC 忽略重写成员函数上的 nodiscard 属性是否正确?

c++ - 寻找一个 MACRO 将两个字符加在一起用于 switch cases

c++ - N3936 第 3.3.7/1 节中的规则 3 是否多余?