Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/141.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 初始值设定项列表中的多个突变是否未定义行为?_C++_C++11_Undefined Behavior_Initializer List - Fatal编程技术网

C++ 初始值设定项列表中的多个突变是否未定义行为?

C++ 初始值设定项列表中的多个突变是否未定义行为?,c++,c++11,undefined-behavior,initializer-list,C++,C++11,Undefined Behavior,Initializer List,我对初始化列表和序列点很好奇。我刚才读到初始化列表中的求值顺序是从左到右。如果是这样的话,那么在评估点之间一定有某种顺序点,我错了吗?那么,下面的代码是否有效?是否有任何东西会导致未定义的行为 int i = 0; struct S { S(...) {} operator int() { return i; } }; int main() { i = S{++i, ++i}; } 欢迎所有回复。是的,代码有效,没有未定义的行为。初始化器列表中的表达式在计算S的构

我对初始化列表和序列点很好奇。我刚才读到初始化列表中的求值顺序是从左到右。如果是这样的话,那么在评估点之间一定有某种顺序点,我错了吗?那么,下面的代码是否有效?是否有任何东西会导致未定义的行为

int i = 0;

struct S {
    S(...) {} 
    operator int() { return i; }
};

int main() {
    i = S{++i, ++i};
}

欢迎所有回复。

是的,代码有效,没有未定义的行为。初始化器列表中的表达式在计算
S
的构造函数之前从左到右求值并排序。因此,您的程序应该一致地将值
2
分配给变量
i

引用C++标准的P.5.5.4:

“在带括号的初始列表的初始值设定项列表中,初始值设定项子句,包括由包扩展(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
    构造函数之后(及其转换运算符),该行为定义良好。

    是的,确实存在未定义行为的情况

    以下是导致未定义行为的情况示例:

    i = v[i++]; // the behavior is undefined
    i = i++ + 1; // the behavior is undefined
    
    • 一个变量在一个序列点内更改多次 作为一个典型的例子,经常引用i=i++表达式,其中 i变量的赋值及其增量在 同时。要了解更多关于此类错误的信息,请阅读本节
    • 在初始化变量之前使用它。发生未定义的行为 尝试使用变量时
    • 使用新[]运算符和后续版本分配内存 使用删除运算符。例如:T*p=newt[10];delete p;。 正确的代码是:T*p=newt[10];delete[]p
    已编辑

    另外,您的代码S{++i,++i};不是为VS2012编译的。您的意思可能是S(++i,++i);?
    如果使用“()”,则会出现未定义的行为。在另一种情况下,源代码不正确。

    您确定这一点吗?在C++03中,
    i=++i;
    (例如)是未定义的行为,即使计算顺序是有保证的,因为序列点之间的
    i
    被修改了两次。我不是C++11专家——我知道C+11抛弃了序列点的概念——但这些例子看起来非常像我的眼睛。@Nemo:C++11仍然有序列的概念,只是比为了解释多个线程,“序列点”的概念。但是,答案缺少了引用的一句话之后更重要的一句话:也就是说,在初始值设定项列表的逗号分隔列表中,与给定初始值设定项子句相关联的每个值计算和副作用都在与任何初始值设定项子句相关联的每个值计算和副作用之前排序。“所以代码确实定义得很好。@尼莫,但是在我的
    int()中没有序列点吗?”
    重载?定义行为?@MikeSeymour:你确定这句话的一部分与此相关吗?它只解释了表达式求值是在初始值设定项列表中排序的,但似乎并没有提到Nemo对
    操作符=
    的左侧和右侧的怀疑。我相信1.9/15解决了这个问题。我错了吗?@Nemo这也是定义良好的C++11之前版本,尽管计算顺序未指定。我创建了一个覆盖此C++11之前版本的示例,因为这是另一个问题中的一个争论源,我自己发现了这一点很有启发性。这里没有未定义的行为:大括号初始化语法是常规的C++11,并且VS2012不支持它,这是IDE的一个限制。当使用大括号初始化时,根据我在回答中引用的标准段落,表达式从左到右求值。其次,他在初始化之前没有使用变量。该变量是全局变量,并且初始化为0。因此,如果是您否决了我的答案,您可能需要重新考虑您的决定:-)此答案对于C++11不正确。直到VS201,C++11才真正得到完全支持