理解lvalue-to-rvalue转换示例

4
我很难理解这段代码(来自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

1
我的猜测是constexpr在编译时被计算并存储在程序的某个只读部分,与堆栈帧无关,而x则在堆栈帧中,访问已删除堆栈帧后的x会导致未定义行为。 - R Sahu
你在哪里找到这个例子的?是在标准中还是其他来源? - Ben Voigt
1
@BenVoigt 这是来自C++14 [conv.lval]/2.2的内容。 - Casey
@BenVoigt,你的评论非常有帮助。最近有一些关于constexpr和ODR的SO问题引起了我的注意,所以我刚开始深入研究ODR规则。相对较新的措辞并不直观,而且现有的解释也不是很好。 - Shafik Yaghmour
@ShafikYaghmour:是的,我真的不喜欢“表达式的潜在结果”这个术语,因为它与同一表达式的“结果”完全无关。 - Ben Voigt
1个回答

8
这是因为y.n没有odr使用,因此不需要访问y.n,odr使用的规则在3.2中有所涵盖,其中提到:
变量x的名称作为一个可能被评估的表达式ex出现时,除非x应用lvalue-to-rvalue转换(4.1)产生了一个不调用任何非平凡函数的常量表达式(5.19),且如果x是一个对象,则ex是表达式e的潜在结果集合的元素,其中应用lvalue-to-rvalue转换(4.1)到e或者e是一个弃值表达式。
请注意,Ben Voigt发表了一些有益的评论,进一步澄清了这一点。因此,工作假设是x将会是:
y

并且e将会是(关于e的不同表达式定义在3.2节第2段中):

(b ? y : x).n

y生成一个常量表达式,且将左值转换为右值应用于表达式e

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

你的例子包含在N39394.1章节中[conv.lval],而在这个例子之前,它说:

当将左值转换为右值应用于表达式e时,如果满足以下条件之一:

并包括以下符号,该示例属于其中之一:

评估e的结果导致集合潜在结果的成员ex的评估,并且ex命名变量x,该变量未被odr使用

那么:

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

这是由于缺陷报告1773而应用于C++14草案标准。


“引用对象中包含的值未被访问”是什么意思?2y.n的值,那么这是否意味着我从g(true)中不会得到2 - template boy
3
调用g(true)函数后,确实会返回2,但程序并不需要访问名为y.n的物理内存位置来获取该值。 - Khouri Giordano

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接