我对初始化列表和序列点很好奇。我之前读到过初始化列表中的评估顺序是从左到右的。如果确实如此,那么在评估点之间必须存在某种序列点,我错了吗?因此,根据上述内容,以下代码是否有效?是否存在任何导致未定义行为的内容?
int i = 0;
struct S {
S(...) {}
operator int() { return i; }
};
int main() {
i = S{++i, ++i};
}
非常感谢任何和所有的回复。
我对初始化列表和序列点很好奇。我之前读到过初始化列表中的评估顺序是从左到右的。如果确实如此,那么在评估点之间必须存在某种序列点,我错了吗?因此,根据上述内容,以下代码是否有效?是否存在任何导致未定义行为的内容?
int i = 0;
struct S {
S(...) {}
operator int() { return i; }
};
int main() {
i = S{++i, ++i};
}
非常感谢任何和所有的回复。
是的,代码是有效的且没有未定义行为。初始化器列表中的表达式从左到右评估并在S
的构造函数执行之前排序。因此,您的程序应该始终将值2
分配给变量i
。
C++标准第8.5.4节的引用:
"在大括号初始化列表的初始化器列表中,包括任何由pack expansions(14.5.3)产生的初始化程序子句,按出现顺序进行评估。也就是说,与给定初始化程序子句相关的每个值计算和副作用都排在初始化程序列表中后跟它的任何初始化程序子句相关的每个值计算和副作用之前。"
因此,发生的情况是:
++i
,得到i = 1
(S
构造函数的第一个参数);++i
,得到i = 2
(S
构造函数的第二个参数);S
的构造函数;S
的转换运算符,返回值2
;2
分配给i
(它已经有了值2
)。标准的另一个相关段落是第1.9/15节,其中还提到了类似的具有未定义行为的示例:
i = v[i++]; // the behavior is undefined
i = i++ + 1; // the behavior is undefined
然而,同一段落中还提到:
"除非特别说明,单个运算符的 操作数和单个表达式的子表达式的评估是未排序的。[...] 在调用函数(无论函数是否为内联函数)时,与任何参数表达式相关联的每个值计算和副作用,或者称为被调用函数的后缀表达式的副作用,都将在执行所调用函数体中的每个表达式或语句之前排序。"
由于1)初始化列表中表达式的评估从左到右排序,2)S
类的构造函数的执行顺序在初始化列表中所有表达式的评估之后,3)对 i
的赋值在执行 S
的构造函数(及其转换运算符)之后排序,因此行为是良好定义的。
确实,您遇到了未定义行为的情况。
以下是导致未定义行为的情况的示例:
编辑
另外,您的代码 S{++i, ++i}; 无法在 VS2012 中编译。也许您的意思是 S(++i, ++i);?如果使用“()”,则存在未定义行为。否则,您的源代码是不正确的。
i = ++i;
(例如)是未定义的行为,即使评估顺序是有保证的,因为i
在序列点之间被修改了两次。我不是C++11专家 - 我知道C++11放弃了序列点的概念 - 但这些示例在我看来非常相似。 - Nemoint()
重载中不是有一个序列点吗?这不是使行为定义明确了吗? - David Gf
是一个普通函数,那么i = f(++i)
是被定义的吗? - Nemo