是否定义了此代码行为?

时间:2011-03-29 11:58:46

标签: c++ operators

以下代码如何打印到控制台?

map<int,int> m;
m[0] = m.size();
printf("%d", m[0]);

可能的答案:

  1. 未定义代码的行为,因为未定义编译器首先执行哪个语句m[0]m.size()。因此,它可以打印1以及0
  2. 打印0,因为首先执行赋值运算符的右侧。
  3. 它打印1,因为operator[]具有完整语句m[0] = m.size()的最高优先级。因此,会发生以下事件序列:

    • m[0]在地图中创建一个新元素
    • m.size()被调用,现在是1
    • m[0]被分配先前返回的内容(通过m.size())1
  4. 真正的答案?,我不知道^^

4 个答案:

答案 0 :(得分:17)

我认为未指定是否在m[0]中存储0或1,但它不是未定义的行为。

LHS和RHS可以按任意顺序发生,但它们都是函数调用,因此它们在开始和结束时都有一个序列点。在没有插入序列点的情况下,他们两个人没有共同访问同一个对象的危险。

赋值是实际的int赋值,而不是具有关联序列点的函数调用,因为operator[]返回T&。这简直令人担忧,但它并没有修改在本声明中任何其他位置访问的对象,所以这也是安全的。当然,它在operator[]内被访问,在它初始化的地方,但是在从operator[]返回的序列点之前发生,所以没关系。如果不是,m[0] = 0;也将是未定义的!

但是,标准未指定operator=的操作数的评估顺序,因此调用size()的实际结果可能为0或1,具体取决于发生的顺序。

但是,以下是未定义的行为。它没有进行函数调用,所以没有任何东西可以阻止size被访问(在RHS上)和修改(在LHS上)没有中间序列点:

int values[1];
int size = 0;

(++size, values[0] = 0) = size;
/*     fake m[0]     */  /* fake m.size() */

答案 1 :(得分:2)

此语句中对operator []的调用与clear的调用之间没有sequence point。因此,行为未定义。

答案 2 :(得分:2)

打印1,而不使用gcc发出警告(!)。 提出警告,因为它未定义。

operator[]operator.的优先级为2,而operator=的优先级为16。 这意味着定义良好,m[0]m.size()将在分配之前执行。但是,没有定义首先执行哪一个。

答案 3 :(得分:1)

鉴于C ++ 17几乎就在这里,我认为值得一提的是,此代码现在在新标准下表现出明确的行为。对于=作为整数的内置赋值的情况:

[expr.ass]/1

  

赋值运算符(=)和复合赋值运算符all   小组从右到左。所有都需要左侧可修改的左值   操作数并返回一个引用左操作数的左值。结果   在所有情况下,如果左操作数是位字段,则是位字段。在所有   在这种情况下,赋值在值的计算之后排序   左右操作数,以及之前的值计算   赋值表达式。 右操作数在左前排序   操作数。关于不确定顺序的函数调用,   复合赋值的操作是单一的评估。

这使我们只有一个选项,那就是#2。