【问题标题】:Unexpected behavior from ternary operator三元运算符的意外行为
【发布时间】:2014-05-13 16:48:22
【问题描述】:

当我运行这段代码时:

#include <iostream>
#include <map>

using namespace std;

void f1() {
    map<int, int> m;
    m[5] = m.find(5) == m.end() ? 1 : 2;
    cout << m[5] << endl;
}
void f2() {
    map<int, int> m;
    if (m.find(5) == m.end())
        m[5] = 1;
    else
        m[5] = 2;
    cout << m[5] << endl;
}
int main() {
    f1();
    f2();
    return 0;
}

我得到以下输出:

2
1

为什么我在使用三元运算符时会得到错误的输出?

【问题讨论】:

  • 你在同一行中做了太多的事情。我强烈建议不要这样做。

标签: c++ ternary-operator


【解决方案1】:

函数调用的顺序是不确定的,会影响您的结果。考虑:

void f1()
{
    map<int, int> m;
    int& result = m[5]; // creates m[5]
    auto it = m.find(5); // finds the empty m[5] created above
    int value = (it == m.end())? 1: 2;
    result = value;
    std::cout << m[5] << std::endl;
}

这是您编写的代码的完全合法的实现。

三元运算符没有错。这样就可以了:

bool notfound = (m.find(5) == m.end());
m[5] = notfound? 1: 2;

重要的是m[5] 是否可能在m.find(5) 之前被评估。

【讨论】:

    【解决方案2】:

    在此声明中

    m[5] = m.find(5) == m.end() ? 1 : 2;
    

    您通过将对象 m[5] 放置在分配的左侧来创建它。所以 m.find(5) != m.end() 因为 m[5] 已经存在。

    实际上存在未定义的行为,因为没有指定赋值的左右操作数的求值顺序。

    【讨论】:

    • 这里不是未定义的行为,因为这些操作符是函数调用,函数调用不能重叠。所以不存在数据竞赛。然而,函数调用的顺序是不确定的。
    • @Ben Voigt 有一个赋值运算符。可以先计算右操作数,也可以先计算左操作数。所以有未定义的行为。
    • 不,没有未定义的行为。此代码保证生成12。在真正的未定义行为的情况下,它可能会产生其他值、崩溃或损坏打开的文件导致计算机无法启动等。
    • 请注意x = ++i + ++i; 是未定义的行为,但int inc(int&amp; v) { return ++v; } 然后x = inc(i) + inc(i); 不是未定义的行为,因为对变量的访问不能再重叠。
    • @Ben Voigt 对不起,我没有理解你的深刻想法。我确信存在未定义的行为,他的示例非常清楚地表明了这一点。他的编译器首先评估左操作数。结果是 m.find(5) != m.end()。两者都不会阻止编译器首先评估正确的操作数。在这种情况下,结果将是另一个。
    猜你喜欢
    • 2017-03-05
    • 1970-01-01
    • 2020-02-08
    • 2023-03-09
    • 1970-01-01
    • 2011-12-31
    • 1970-01-01
    • 2014-01-18
    • 2022-01-11
    相关资源
    最近更新 更多