增加在初始化列表中使用两次的变量 - 未定义的行为?

Ray*_*mel 16 c c++ initializer undefined-behavior

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

这个语句定义得很好,或者在初始化列表中使用前缀增量运算符,对于在列表中出现两次的变量,未定义的行为?

struct T t = { i, ++i };
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

struct T t = { i++, i++ };
Run Code Online (Sandbox Code Playgroud)

son*_*yao 14

C++ 11及更高版本

该行为已明确定义列表初始化.根据先前排序的规则(自C++ 11起):

10)在列表初始化中,给定初始化子句的每个值计算和副作用在每个值计算和副作用之前按照括号括起的逗号分隔的初始化列表中的任何初始化子句关联.

因此struct T t = { i, ++i };,i将首先评估,然后++i,订单是明确定义的.而所有其他样本也会很好.

来自C++标准的报价,$ 8.6.4/4 List-initialization [dcl.init.list]:

(强调我的)

在braced-init-list的初始化列表中,初始化子句(包括包扩展([temp.variadic])的任何结果)按照它们出现的顺序进行评估.也就是说,与给定初始化子句相关联的每个值计算和副作用在每个值计算和副作用之前与在初始化列表的逗号分隔列表中跟随它之后的任何初始化子句相关联.[注意:无论初始化的语义如何,此评估顺序都保持不变; 例如,当initializer-list的元素被解释为构造函数调用的参数时,它适用,即使通常对调用的参数没有排序约束. - 结束说明]


Jon*_*ler 13

C

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

C11标准,ISO/IEC 9899:2011,在第6.7.9节初始化中说明:

19初始化应在初始化器列表顺序中进行,每个初始化器为特定子对象提供,覆盖同一子对象的任何先前列出的初始化器; 151)

151)子对象的任何初始化程序被覆盖并因此不用于初始化该子对象可能根本不会被评估.

听起来很有希望,但......

23初始化列表表达式的评估相对于彼此不确定地排序,因此未指定任何副作用发生的顺序.152)

152)特别是,评估顺序不必与子对象初始化的顺序相同.

因此,(在C中)评估的顺序是不确定的顺序,并且您不能依赖于何时发生增量(或者,在极端情况下,问题中的代码未说明,是否发生增量).

在C99(ISO/IEC 9899:1999)中,部分编号是§6.7.8,但第19和23段的内容基本相同,但脚注编号不同.

在C90(ISO/IEC 9899:1990)中,没有明确解决该问题.

C++

宋元尧回答来看,C++ 11(及以后)中的规则与C11中的规则不同.这种事情强调语言C和C++是不同的,并且使用两种语言标记的问题的写作综合答案非常困难.

密切相关的问题

++除了初始化器之外的上下文中,至少还有两个与副作用相关的问题(例如).它们都应该被阅读.特别是第二个是C++用户感兴趣的; 第一个是标记C而不是C++,因此与那些对C感兴趣的人最相关.

commentsάνταῥεῖ在评论中指出了两者.


M.M*_*M.M 6

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

初始化列表表达式的评估相对于彼此不确定地排序,因此未指定任何副作用发生的顺序.

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

当A在B之前或之后进行测序时,评估A和B是不确定的,但是未指定哪一个.

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