【问题标题】:Understanding the example on lvalue-to-rvalue conversion了解左值到右值转换的示例
【发布时间】:2015-04-14 21:25:52
【问题描述】:

我很难理解这段代码(来自 C++14 草案标准 [conv.lval] 的示例)如何调用 g(false) 的未定义行为。为什么constexpr 使程序有效?

另外,“不访问y.n”是什么意思?在对g() 的两次调用中,我们都返回了n 数据成员,那么为什么最后一行说它不能访问它呢?

struct S { int n; };
auto f() {
    S x { 1 };
    constexpr S y { 2 };
    return [&](bool b) { return (b ? y : x).n; };
}
auto g = f();
int m = g(false); // undefined behavior due to access of x.n outside its
                  // lifetime
int n = g(true);  // OK, does not access y.n

【问题讨论】:

  • 我的猜测是 constexpr 在编译时被评估并存储在程序的某个只读部分中,与堆栈帧无关,而 x 在堆栈帧中,并访问它删除堆栈帧后会导致未定义的行为。
  • 你在哪里找到这个例子?在标准中,还是在其他来源中?
  • @BenVoigt 它来自 C++14 [conv.lval]/2.2。
  • @BenVoigt 您的 cmets 非常有帮助。实际上,由于我最近对 ​​constexpr 和 ODR 提出的一些 SO 问题,我才开始深入研究 ODR 规则。相对较新的措辞不是直截了当的,那里的一些解释也不是很好。
  • @ShafikYaghmour:是的,我真的不喜欢“表达式的潜在结果”这个词,它与同一个表达式的“结果”完全无关。

标签: c++ language-lawyer c++14 constexpr lvalue-to-rvalue


【解决方案1】:

这是因为 y.n 未使用 odr,因此不需要访问 y.n3.2 中介绍了使用 odr 的规则,并表示:

名称显示为潜在求值表达式 ex 的变量 x 是 odr-used 除非应用 左值到右值的转换 (4.1) 到 x 产生一个常量表达式 (5.19),它不会调用任何非平凡的 函数,如果 x 是一个对象,ex 是表达式 e 的潜在结果集合中的一个元素,其中 左值到右值的转换 (4.1) 应用于 e,或者 e 是丢弃值表达式

注意,Ben Voigt 制作了一些有用的 cmets,稍微澄清了这一点。所以这里的工作假设是 x 将是:

y

e 将是(定义 e 的不同表达式包含在第 3.2 节的第 2 段中):

(b ? y : x).n

y 产生一个常量表达式,左值到右值的转换应用于表达式 e

由于f 产生一个通过引用捕获f 的局部变量的lambda,一旦对f 的调用完成,x 就不再有效,因为xf 中的一个自动变量。由于y 是一个常量表达式,它的作用就像y.n 未被访问,因此我们没有相同的生命周期问题。

您的示例包含在N3939 部分4.1 [conv.lval] 并且在该示例之前它说:

当左值到右值的转换应用于表达式 e 时,或者

并包括该示例所属的以下项目符号:

对 e 的评估导致对 e 的一组潜在结果的成员 ex 的评估,并且 ex 为 ex (3.2) 不被 odr-used 使用的变量 x 命名,

然后:

被引用对象中包含的值未被访问

由于defect report 1773,这已应用于 C++14 草案标准。

【讨论】:

  • “被引用对象中包含的值未被访问”是什么意思? 2y.n 的值,这是否意味着我没有从 g(true) 得到 2
  • 您确实从 g(true) 得到了 2,但程序不必访问名为 y.n 的物理内存位置来获取该值。
猜你喜欢
  • 2017-11-24
  • 1970-01-01
  • 2013-07-12
  • 1970-01-01
  • 2014-01-17
  • 1970-01-01
  • 2020-09-19
  • 1970-01-01
  • 2017-08-18
相关资源
最近更新 更多