【问题标题】:Are multiple mutations of the same variable within initializer lists undefined behavior pre C++11初始化程序中同一变量的多个突变是否列出了 C++11 之前的未定义行为
【发布时间】:2013-11-21 19:20:03
【问题描述】:

考虑以下代码:

int main()
{
    int count = 0 ;
    int arrInt[2] = { count++, count++ } ;

    return 0 ;
}

如果我们使用clang -std=c++03 编译代码,它会产生以下警告(live example):

warning: multiple unsequenced modifications to 'count' [-Wunsequenced]
    int arrInt[2] = { count++, count++ } ;
                           ^        ~~

我不提倡使用这样的代码,但类似的代码出现在另一个问题中,并且对于它是否定义根据标准 pre-C++11 存在分歧。在 C++11 中,这种行为是根据Are multiple mutations within initializer lists undefined behavior 明确定义的行为,实际上如果我使用-std=c++11,那么警告就会消失。

如果我们查看之前的C++11 draft standard,它没有覆盖 initializer-list 的相同语言,所以看起来我们只剩下 @ 987654328@ Expressions 段落 4 说:

除非另有说明,单个运算符的操作数和单个表达式的子表达式的求值顺序以及副作用发生的顺序是未指定的。57) 在上一个序列和下一个序列之间一个标量对象的存储值最多只能通过表达式的评估修改一次。此外,只能访问先验值以确定要存储的值。对于完整表达式的子表达式的每个允许排序,都应满足本段的要求;否则行为未定义。

为了使其未定义,我们似乎必须将count++, count++ 解释为一个表达式,因此每个count++ 都作为一个子表达式,那么这段代码是undefined pre-C++11吗?

【问题讨论】:

    标签: c++ language-lawyer undefined-behavior initializer-list


    【解决方案1】:

    代码不是未定义 pre-C++11,但评估顺序是未指定。如果我们看一下草案标准部分1.9程序执行12段说:

    完整表达式是不是另一个表达式的子表达式的表达式。 [...]

    15段说:

    每个完整表达式的求值完成时都有一个序列点12)

    那么问题是count++, count++ 是否是一个完整的表达式 并且每个count++ 是一个子表达式 还是每个count++ 是它自己的完整的表达式,因此每个之后都有序列点?如果我们从8.5 部分查看此初始化的语法Initializers

    initializer-clause:
      assignment-expression
      { initializer-list ,opt }
      { }
    initializer-list:
      initializer-clause
      initializer-list , initializer-clause
    

    我们唯一的表达式是一个assignment-expression,分隔组件的,initializer-list的一部分,并且并且不是表达式的一部分,因此每个count++都是一个完整的表达式,每个之后都有一个序列点

    这种解释得到了以下gccbug report 的证实,它与我的代码非常相似(在我发现这个错误报告之前,我想出了我的示例方式):

    int count = 23;
    int foo[] = { count++, count++, count++ };
    

    最后是defect report 430,我将引用它:

    [...]我相信标准很清楚,上面的每个初始化表达式都是完整表达式(1.9 [intro.execution]/12-13;另见 issue 392),因此有一个序列点在每个表达式之后(1.9 [intro.execution]/16)。我同意该标准似乎没有规定评估表达式的顺序,也许它应该。有谁知道编译器不会从左到右计算表达式?

    【讨论】:

    • 有趣。似乎有一点很重要,即真正的初始化列表中的表达式被认为是完整表达式,因为找不到任何名称中包含“表达式”的更大结构。这是相关的,因为在语法上 initializer-list 也用于 expression-list,这也发生在例如函数调用中。 is 是一个封闭表达式(完整函数调用的 postfix-expression),因此单个参数不被视为完整表达式,并且后面没有序列点他们。
    • @MarcvanLeeuwen 是正确的,我总是想添加一个部分来解释为什么这与函数调用不同。我很高兴您能够自己弄清楚这一点,这总是一种有益的体验。
    猜你喜欢
    • 2023-03-03
    • 2021-09-07
    • 1970-01-01
    • 2016-01-28
    • 1970-01-01
    • 2021-08-30
    • 1970-01-01
    • 1970-01-01
    • 2020-01-18
    相关资源
    最近更新 更多