【问题标题】:Ternary operator gives unexpected results in return statement三元运算符在 return 语句中给出了意外的结果
【发布时间】:2019-03-06 19:40:23
【问题描述】:

我从三元运算符中得到了非常奇怪的结果,它应该返回对链表成员对象的引用。我的数据库对象中有这样的成员函数:

Month& GetLastMonth() { return months_.Size() > 0 ? months_.Last() : Month(); }

我在代码中调用了这个函数 2 次:在加载 DB 以获取信息之后,以及在更改月份的某些参数并按下按钮时。

我第一次将它写入 const 引用,这里没什么大不了的。但是第二次我需要非常量引用来重新计算月份(所以它也会在数据库中改变)。

但是,一些黑魔法发生了,而不是预期的结果。每次我调用GetLastMonth() 函数时,它都会返回一个具有不同地址的引用。因此,在 DB 加载之后 - 它的 1 个地址,重新计算月份 - 它的第 2 个地址,并且在保存到它的第 3 个地址之后。当然,正因为如此,我重新计算月份后的更改不会保存到文件中。

现在,months_ 的大小始终为 1(用于测试目的)。它也是链表,所以 Month 永远不能重新分配。我已经测试过了,它从不调用Month(),所以看起来三元运算符工作正常。

这样调用,结果相同:

Month& GetLastMonth() { return months_.Size() ? months_.Last() : Month(); }

但是如果我不使用三进制来称呼它:

Month& GetLastMonth() { return months_.Last(); }

或者用正确的if():

Month& GetLastMonth()
{ 
    if(months_.Size() > 0)
    {
        return months_.Last();
    }
    return Month();
}

它按预期工作,并返回所有 3 次相同地址的引用。我在考虑这个晦涩的事情大约 2 天,但仍然找不到任何背后的原因。

编辑: 相关问题:Return type of '?:' (ternary conditional operator)

【问题讨论】:

  • 请注意,返回 Month() 是特定于 MS 的扩展(因为它会绑定到非 const 左值引用)。它不适用于标准 C++。
  • 您可能应该将返回类型更改为Month,而不是Month&,因为您正在返回一个临时的。
  • @tadman OP 想要非常量引用。
  • @tadman 是的,我现在看到返回临时对象作为参考确实是个坏主意...我将尝试重构代码,并想出一些更好的方法来防止访问空列表...
  • 一种更 C++ 的方法是返回一个可能等于 months.end() 的迭代器,例如没有找到条目,或者抛出异常。如果这是一个容器,使其符合 C++ 容器的期望和标准通常是一个好主意。

标签: c++ reference return ternary-operator


【解决方案1】:

三元运算符找到两个操作数的共同类型值类别,而这在其他两种情况下不会发生,这就是它在那里工作的原因。

months_.Last()Month() 都有Month 类型,所以那里一切正常。但是现在,让我们检查一下价值类别。 months_.Last() 是左值,而 Month() 是纯右值!因此,这里的公共值类别是纯右值,两个操作数都转换为纯右值。这意味着您每次(来自副本)都从months_.Last() 获得一个新的Month

但请再次注意,这是特定于 MS 的扩展。如果没有该扩展,代码将无效,因为您将尝试将条件运算符返回的纯右值绑定到非 const 左值引用。

【讨论】:

  • 哇...我不知道prvalue存在并且对三元运算符的这种特定行为也有0个想法...需要深入研究这个主题...现在,我肯定会尝试对三元-es更加小心......
  • 所以,如果我理解正确的话,三元只是在物理上无法将纯右值转换为左值,所以它可以做它可以做的——通过复制将左值转换为纯右值?
  • @ScienceDiscoverer 存在从右值到左值的转换,但这里没有发生(对于三元)。是的。这种转换称为左值到右值转换,如果它是一个类,它确实会复制对象。
  • 三元运算符优先选择左值到右值转换是一些特定规则吗?
  • @ScienceDiscoverer 是的。 (甚至不考虑其他转换)。这样它就可以很好地保证复制省略(如果不需要,则无需在三元组中创建对象 - 纯右值不是对象)。
猜你喜欢
  • 2016-06-28
  • 1970-01-01
  • 2011-04-24
  • 2022-01-11
  • 2012-04-03
  • 2012-12-11
  • 1970-01-01
  • 2012-04-09
  • 1970-01-01
相关资源
最近更新 更多