akr*_*ki1 17 c++ operator-precedence language-lawyer
map<int, int> mp;
printf("%d ", mp.size());
mp[10]=mp.size();
printf("%d\n", mp[10]);
Run Code Online (Sandbox Code Playgroud)
此代码产生的答案不是很直观:
0 1
我理解为什么会发生这种情况 - 赋值的左侧返回对mp[10]
底层值的引用,同时创建上述值,然后使用新计算size()
的映射来评估右侧.
这种行为在C++标准中是否有任何说明?或者评估顺序是否未定义?
使用g ++ 5.2.1获得结果.
Sha*_*our 18
是的,这是标准所涵盖的,它是未指定的行为.最近的C++标准提案中涵盖了这一特殊情况:N4228:精炼了Idiomatic C++的表达式评估顺序,旨在优化评估规则的顺序,使其适用于某些情况.
它描述了这个问题如下:
表达式评估顺序是C++社区中反复出现的讨论主题.简而言之,给定诸如 f(a,b,c)的表达式,子标记f,a,b,c的计算顺序 未被标准指定.如果这些子表达式中的任何两个碰巧修改同一个对象而没有插入序列点,则程序的行为是不确定的.例如,表达式 f(i ++,i) 其中i是整数变量导致未定义的行为, v [i] = i ++也是如此.即使行为未定义,评估表达式的结果仍然可以是任何人的猜测.考虑以下程序片段:
Run Code Online (Sandbox Code Playgroud)#include <map> int main() { std::map<int, int> m; m[0] = m.size(); // #1 }
在评估标记为#1的语句后,地图对象应该是什么样子?{{0,0}}或{{0,1}}?
我们知道,除非指定子表达式的评估未被排序,否则这来自草案C++ 11标准部分1.9
程序执行,其中说:
除非另有说明,否则对个体操作员的操作数和个别表达式的子表达式的评估是不确定的.[...]
所有的5.17
赋值和复合赋值运算符[expr.ass]都是:
[...]在所有情况下,赋值在右和左操作数的值计算之后,以及赋值表达式的值计算之前排序.[...]
因此,这部分没有明确评价的顺序,但我们知道这是由于双方没有未定义的行为operator []
,并size()
为函数调用和部分1.9
告诉我们(重点煤矿):
[...]当调用函数时(无论函数是否为内联函数),在执行每个表达式或语句之前,对与任何参数表达式或指定被调用函数的后缀表达式相关联的每个值计算和副作用进行排序.在被调用函数的主体中.[注意:与不同参数表达式相关的值计算和副作用未被排序.-end note] 调用函数(包括其他函数调用)中的每个评估在执行被调用函数体之前或之后没有特别排序,对被调用函数的执行有不确定的顺序. ..]
注意,我N4228
在问题中提出了第二个有趣的例子."C++编程语言"第4版第36.3.6节中的代码是否有明确定义的行为?.
这似乎是一个修订版N4228
是在最后WG21会议上演进工作组接受但纸(P0145R0)尚未公布.所以这在C++ 17中可能不再是未指定的.
的修订3 p0145此指定,并且更新由[expr.ass] P1:
赋值运算符(=)和复合赋值运算符都是从右到左分组.所有都需要一个可修改的左值作为左操作数; 它们的结果是左值操作数的左值.如果左操作数是位字段,则所有情况下的结果都是位字段.在所有情况下,在右和左操作数的值计算之后,以及在赋值表达式的值计算之前,对赋值进行排序.右操作数在左操作数之前排序....
从C++ 11标准(强调我的):
5.17赋值和复合赋值运算符
1赋值运算符(=)和复合赋值运算符从右到左分组.所有都需要一个可修改的左值作为左操作数,并返回一个左值操作数的左值.如果左操作数是位字段,则所有情况下的结果都是位字段.在所有情况下,在右和左操作数的值计算之后,以及在赋值表达式的值计算之前,对赋值进行排序.
是否首先评估左操作数或首先评估右操作数不是由语言指定的.编译器可以自由选择首先评估任一操作数.由于代码的最终结果取决于操作数的评估顺序,我会说它是未指定的行为而不是未定义的行为.
1.3.25未指明的行为
行为,对于格式良好的程序构造和正确的数据,取决于实现
归档时间: |
|
查看次数: |
2241 次 |
最近记录: |