问题 “最重要的const”有条件表达式吗?


请考虑以下代码:

int foo(MyClass const* aPtr = 0) {
    MyClass const& a = aPtr ? *aPtr : MyClass(); // Either bind to *aPtr, or to a default-constructed MyClass
    ...
    return a.bar();
}

“最重要的常数” 希望在这里使用。目的是允许null aPtr 传入(BTW,是的,它必须是指针参数),在这种情况下是临时的 MyClass object将是默认构造的,并且它的生命周期由const引用绑定到它。然而,如果 aPtr 如果不为null,引用将绑定到其指向的对象,而不会发生任何(昂贵的)复制构造。

问题二是:

  1. 如果 aPtr == 0是的 a 保证引用有效 MyClass 对象直到函数结束?
  2. 如果 aPtr != 0,将 a 绑定它,而不是其他一些 MyClass

根据测试,1的答案几乎肯定是“是”。 #2我不太确定,但是(复制省略等)......条件表达式似乎有可能最终复制构造一个临时的 MyClass 从 *aPtr,延长寿命  临时。


7945
2018-05-27 17:51


起源

(1)穷人 aPtr == 0 结束了一生 a 一开始 ; - Dieter Lücking


答案:


您的条件表达式是一个prvalue(因为它的一个操作数是)。如果选择了条件运算符的第一个替代项,则将其转换为临时(产生副本)。该临时性与参考绑定,并且适用通常的生命期延长。

相关标准[expr.cond]:

如果操作数具有类类型,则结果是结果类型的prvalue临时值,它根据第一个操作数的值从第二个操作数或第三个操作数进行复制初始化。


6
2018-05-27 17:55



说得通。我想,这太好了。 - Sneftel
解决方法: static MyClass default; a = *(aptr ? aptr : &default); - Mark Ransom
@MarkRansom啊,我喜欢。如果默认构造/销毁是昂贵的,那么它可能比最初的方法更有效。 - Sneftel
......并且“通常的终身延长”以分号结束。 - Dieter Lücking
@DieterLücking不,不,看到我的回答。 - Barry


首先,是的 a 保证是指有效的 MyClass 目的。这直接来自[class.temporary] / 4-5:

在两种情况下,临时表在与完全表达结束时不同的地方被摧毁。第一个上下文是在调用默认构造函数时[...]

第二个上下文是引用绑定到临时的。引用的临时值   绑定或临时,它是绑定引用的子对象的完整对象 仍然存在   在参考的生命周期 除:

  • 临时绑定到构造函数中的引用成员 构造函数初始化程序 [...]
  • 临时绑定到函数调用中的引用参数[...]
  • 在函数返回语句中临时绑定到返回值的生命周期[...]
  • 临时绑定到a中的引用 新初始化 [...]

这些例外都不适用。

如果 aPtr 是一个有效的指针,然后复制是因为类型 aPtr ? *aPtr : MyClass{} 只是 MyClass。这种临时性受到约束 a,和 它的 一生也因同样的原因而持续存在。


3
2018-05-27 17:55





关于1)见上面的kerek答案

关于2)标准说关于条件运算符:

5.16 / 4: 如果第二个和第三个操作数是glvalues的 相同的价值类别 并拥有 相同的类型,结果是该类型和值类别(...)。

5.16 / 5: 否则,结果是prvalue。 (......)

根据中国的左撇子和左撇子的分类 3.10 / 1*aPtr 是一个左值, MyClass() 是一个prvalue。因此,结果应该是一个prvalue,以便引用应该引用此临时(可能是复制构造的temp)。

编辑: 在这里 在线演示,这表明const引用引用了一个临时的而不是指向aPtr指向的原始对象。


1
2018-05-27 18:20



1) MyClass() 是一个prvalue。 2)“值类别”是左值,右值和右值之一。 - T.C.
@ T.C。如果prvalue不能在作业的左侧网站上,为什么呢 这个 编译成功?我只是想确定,因为我总是对xvalues犹豫不决。 - Christophe
谁说prvalues不能在作业的lhs?只有内置赋值运算符需要一个可修改的左值作为其左操作数。 - T.C.