三元运算符在 return 语句中给出意外结果

Ternary operator gives unexpected results in return statement

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

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

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

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

但是,一些黑魔法发生了,而不是预期的结果。每次我调用 GetLastMonth() 函数时,它 return 都是一个具有不同地址的引用。因此,在数据库加载后 - 它的第 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();
}

它按预期工作并且 returns 引用了 3 次相同的地址。我在想这个晦涩的东西大约 2 天,但仍然找不到任何背后的原因。

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

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

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

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