是什么使i = i ++ +1;在C ++ 17中合法吗? [英] What made i = i++ + 1; legal in C++17?

查看:95
本文介绍了是什么使i = i ++ +1;在C ++ 17中合法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在开始大叫未定义的行为之前,明确地列出了 N4659(C ++ 17)

Before you start yelling undefined behaviour, this is explicitly listed in N4659 (C++17)

  i = i++ + 1;        // the value of i is incremented

N3337(C ++ 11)

  i = i++ + 1;        // the behavior is undefined

发生了什么变化?

据我所知,来自 [N4659 basic.exec]

除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的求值是无序列的.运算符的操作数的计算在运算符结果的值计算之前进行排序.如果相对于同一内存位置上的另一副作用或使用同一内存位置中任何对象的值进行的值计算相对于某个内存位置的副作用未进行排序,并且这些潜在的并发事件不是潜在的并发,则该行为不确定. /p>

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [...] The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. If a side effect on a memory location is unsequenced relative to either another side effect on the same memory location or a value computation using the value of any object in the same memory location, and they are not potentially concurrent, the behavior is undefined.

[N4659 basic.type]

对于普通可复制类型,值表示形式是对象表示形式中确定""的一组位,""是实现定义的一组值中的一个离散元素

For trivially copyable types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values

来自 [N3337 basic.exec]

除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的求值是无序列的.运算符的操作数的计算在运算符结果的值计算之前进行排序.如果相对于同一标量对象的另一副作用或使用同一标量对象的值进行的值计算,未对标量对象的副作用进行排序,则该行为是不确定的.

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [...] The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

同样,值定义在 [N3337 basic.type]

对于普通可复制类型,值表示形式是对象表示形式中一组确定 value 的位,该值是实现定义的一组值中的一个离散元素.

For trivially copyable types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values.

除了提到无关紧要的并发性之外,它们是相同的,并且使用了内存位置代替了标量对象,其中

They are identical except mention of concurrency which doesn't matter, and with the usage of memory location instead of scalar object, where

算术类型,枚举类型,指针类型,成员类型的指针,std::nullptr_t和这些类型的cv限定版本统称为标量类型.

Arithmetic types, enumeration types, pointer types, pointer to member types, std::nullptr_t, and cv-qualified versions of these types are collectively called scalar types.

不影响该示例.

来自 [N4659 expr.ass]

赋值运算符(=)和复合赋值运算符都从右到左分组.它们都需要一个可修改的左值作为其左操作数,并返回一个引用左操作数的左值.如果左操作数是位字段,则所有情况下的结果都是位字段.在所有情况下,赋值都在左右操作数的值计算之后和赋值表达式的值计算之前进行排序.右边的操作数在左边的操作数之前排序.

The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand. The result in all cases is a bit-field if the left operand is a bit-field. In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression. The right operand is sequenced before the left operand.

来自 [N3337 expr.ass]

赋值运算符(=)和复合赋值运算符都从右到左分组.它们都需要一个可修改的左值作为其左操作数,并返回一个引用左操作数的左值.如果左操作数是位字段,则所有情况下的结果都是位字段.在所有情况下,赋值的顺序在左右操作数的值计算之后,在赋值表达式的值计算之前.

The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand. The result in all cases is a bit-field if the left operand is a bit-field. In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.

唯一的区别是N3337中没有最后一个句子.

The only difference being the last sentence being absent in N3337.

最后一个句子不应该具有任何重要性,因为左操作数i既不是另一个副作用" 也不是使用相同标量对象的值" ,因为 id-expression 是一个左值.

The last sentence however, shouldn't have any importance as the left operand i is neither "another side effect" nor "using the value of the same scalar object" as the id-expression is a lvalue.

推荐答案

在C ++ 11中,赋值"的行为(即修改LHS的副作用)在值计算之后进行排序.请注意,这是一个相对弱"的保证:它仅与RHS的值计算相关地产生排序.它没有提及RHS中可能出现的副作用,因为副作用的出现不是值计算的一部分. C ++ 11的要求在分配行为和RHS的任何副作用之间没有建立相对的顺序.这就是为UB创造潜力的原因.

In C++11 the act of "assignment", i.e. the side-effect of modifying the LHS, is sequenced after the value computation of the right operand. Note that this is a relatively "weak" guarantee: it produces sequencing only with relation to value computation of the RHS. It says nothing about the side-effects that might be present in the RHS, since occurrence of side-effects is not part of value computation. The requirements of C++11 establish no relative sequencing between the act of assignment and any side-effects of the RHS. This is what creates the potential for UB.

在这种情况下,唯一的希望是RHS中使用的特定运营商提供的任何其他保证.如果RHS使用前缀++,则在本示例中,特定于前缀形式++的排序属性将节省大量时间.但是后缀++是另一回事:它没有做出这样的保证.在C ++ 11中,在此示例中,=和后缀++的副作用最终没有相互关联.那就是UB.

The only hope in this case is any additional guarantees made by specific operators used in RHS. If the RHS used a prefix ++, sequencing properties specific to the prefix form of ++ would have saved the day in this example. But postfix ++ is a different story: it does not make such guarantees. In C++11 the side-effects of = and postfix ++ end up unsequenced with relation to each other in this example. And that is UB.

在C ++ 17中,在赋值运算符的规范中增加了一个额外的句子:

In C++17 an extra sentence is added to the specification of assignment operator:

右操作数在左操作数之前排序.

The right operand is sequenced before the left operand.

结合以上所述,可以提供非常有力的保证.它对在RHS中发生的一切(包括任何副作用)进行排序,然后在LHS中发生的一切中进行排序.由于实际分配是在LHS(和RHS)之后进行 排序的,因此额外的排序将分配行为与RHS中存在的任何副作用完全隔离开来.这种更强大的排序消除了上面的UB.

In combination with the above it makes for a very strong guarantee. It sequences everything that happens in the RHS (including any side-effects) before everything that happens in the LHS. Since the actual assignment is sequenced after LHS (and RHS), that extra sequencing completely isolates the act of assignment from any side-effects present in RHS. This stronger sequencing is what eliminates the above UB.

(已更新,以考虑到@John Bollinger的评论.)

(Updated to take into account @John Bollinger's comments.)

这篇关于是什么使i = i ++ +1;在C ++ 17中合法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆