对初始化列表中使用了两次的变量进行递增——未定义的行为

Incrementing a variable used twice in an initializer list - undefined behavior?

本文关键字:变量 未定义 两次 列表 初始化      更新时间:2023-10-16

编辑:尚未回答-链接的问题是关于普通r值,初始化器列表是一个单独的,如果相关的概念。

语句是否定义良好,或者在初始化列表中对出现两次的变量使用前缀自增操作符是否为未定义行为?

struct T t = { i, ++i };

我对ANSI C最感兴趣,但知道其他版本的C和/或c++是否不同也很有用。如果下面的类似结构是合法的:

struct T t = { i, i++ };
struct T t = { ++i, ++i };
struct T t = { i++, ++i };
struct T t = { i++, i++ };

c++ 11后来

为列表初始化定义了行为。根据顺序优先规则(c++ 11起):

10)在列表初始化中,给定初始化子句的每个值计算和副作用都排在其后的任何初始化子句的值计算和副作用之前,在用大括号括起来的逗号分隔的初始化子句列表中。

所以对于struct T t = { i, ++i };,先求i,再求++i,顺序是明确的。所有其他的样品也会很好。

引用自c++标准,$8.6.4/4 List-initialization[dcl.init.list]:

(强调我的)

在大括号初始化列表的初始化器列表中初始化子句,包括任何由包展开产生的子句([temp.variadic]), 按照它们出现的顺序求值。也就是说,与a相关的每个值的计算和副作用给定的初始化子句在每次值计算之前进行排序以及与它后面的任何初始化子句相关的副作用在初始化器列表的逗号分隔列表中。[注:此的语义如何,求值顺序都保持不变初始化;例如,它适用于初始化列表被解释为构造函数调用的参数,尽管通常没有排序限制调用的参数。

C

在C中(不一定与c++中的答案相同),没有与初始化列表的组件相关联的序列点。

C11标准,ISO/IEC 9899:2011,在§6.7.9 初始化:

初始化应按照初始化器列表顺序进行,为特定子对象提供的每个初始化器覆盖先前列出的同一子对象的初始化器;<一口> 151)

151)子对象的任何初始化式,如果被重写而不是用于初始化该子对象,则可能根本不会求值。

听起来很有希望,但是…

初始化列表表达式的求值彼此之间的顺序是不确定的,因此任何副作用发生的顺序是未指定的。152)

152)特别是,求值顺序不必与子对象初始化顺序相同。

因此,(在C中)求值顺序是不确定的,您不能依赖于何时发生增量(或者,在问题中的代码没有说明的极端情况下,是否发生增量)。

在C99 (ISO/IEC 9899:1999)中,章节编号为§6.7.8,但第19段和第23段基本上具有相同的内容,除了脚注编号不同。

在C90 (ISO/IEC 9899:1990)中,这个问题没有明确地解决。

c++

从songyuanyao的回答来看,c++ 11(及以后版本)中的规则与C11中的规则不同。这类事情强调了C和c++是不同的语言,使得为用这两种语言标记的问题编写全面的答案变得极其困难。

密切相关的问题

在初始化器以外的上下文中,至少还有两个与副作用相关的问题(比如++)。这两本书也都值得一读。第二种是c++用户特别感兴趣的;第一个标签是C,而不是c++,因此与那些对C感兴趣的人最相关。

  • 为什么这些结构(使用++)未定义的行为?

  • 未定义行为和序列点

两者都在评论中用π α ντα ρ ε ε指出。

在C11中,所有这些初始化的行为都不是未定义的。看到6.7.9/23:

初始化列表表达式的求值彼此之间的顺序是不确定的,因此任何副作用发生的顺序是未指定的。

术语不确定排序定义如下(5.1.2.3):

当A被排序时,计算值A和B是不确定排序的在B之前或之后,但未指定是哪个

在C99中,所使用的语言并没有明确说明它是相同的情况,还是未定义的行为。在C89中根本没有提到这个问题,所以我们应该假设在C89中这些都是未定义的。