C++ 17引入了哪些评估顺序保证?

Joh*_*erg 77 c++ operator-precedence c++17

在典型的C++代码中,C++ 17评估顺序保证(P0145)投票的含义是什么?

对于像这样的事情,它有什么变化

i=1;
f(i++, i)
Run Code Online (Sandbox Code Playgroud)

std::cout << f() << f() << f() ;
Run Code Online (Sandbox Code Playgroud)

要么

f(g(),h(),j());
Run Code Online (Sandbox Code Playgroud)

Joh*_*erg 66

到目前为止,评估订单未指定的一些常见情况已指定且有效C++17.现在,未指定某些未定义的行为.

那样的事情呢

i=1;
f(i++, i)
Run Code Online (Sandbox Code Playgroud)

未定义,但现在未指定.

std::cout << f() << f() << f() ;
Run Code Online (Sandbox Code Playgroud)

未指定,但将与运算符优先级兼容,以便第一次评估f将在流中首先出现.(以下示例).

f(g(),h(),j());
Run Code Online (Sandbox Code Playgroud)

仍然有未指定的g,h,j的评估顺序.请注意,对于之前要评估i++的规则.if(1, 1)

另请注意提案文本中的以下示例:

 std::string s = "but I have heard it works even if you don't believe in it" 
 s.replace(0, 4, "").replace(s.find("even"), 4, "only")
  .replace(s.find(" don't"), 6, "");
Run Code Online (Sandbox Code Playgroud)

该示例来自C++编程语言,第4版,Stroustrup,并且过去是未指定的行为,但使用C++ 17它将按预期工作.可恢复功能(f(1, 2))存在类似问题.

作为另一个例子,请考虑以下事项:

#include <iostream>
#include <string>
#include <vector>
#include <cassert>

struct Speaker{
    int i =0;
    Speaker(std::vector<std::string> words) :words(words) {}
    std::vector<std::string> words;
    std::string operator()(){
        assert(words.size()>0);
        if(i==words.size()) i=0;
        // pre- C++17 version:
        auto word = words[i] + (i+1==words.size()?"\n":",");
        ++i;
        return word;
        // Still not possible with C++17:
        // return words[i++] + (i==words.size()?"\n":",");

    }   
};

int main() {
    auto spk = Speaker{{"All", "Work", "and", "no", "play"}};
    std::cout << spk() << spk() << spk() << spk() << spk() ;
}
Run Code Online (Sandbox Code Playgroud)

使用C++ 14之前,我们可能(并且将会)获得诸如此类的结果

play
no,and,Work,All,
Run Code Online (Sandbox Code Playgroud)

代替

All,work,and,no,play
Run Code Online (Sandbox Code Playgroud)

注意,上面的效果与之相同

(((((std::cout << spk()) << spk()) << spk()) << spk()) << spk()) ;
Run Code Online (Sandbox Code Playgroud)

但是,在C++ 17之前,并不能保证第一次调用会首先进入流.

参考文献:从接受的提案中:

后缀表达式从左到右进行计算.这包括函数调用和成员选择表达式.

分配表达式从右到左进行评估.这包括复合作业.

从左到右评估移位运算符的操作数.总之,以下表达式按a,然后b,然后是c,然后d来计算:

  1. AB
  2. A-> B
  3. 一个 - >*B
  4. a(b1,b2,b3)
  5. b @ = a
  6. 一个并[b]
  7. a << b
  8. a >> b

此外,我们建议使用以下附加规则:涉及重载运算符的表达式的求值顺序由与相应内置运算符关联的顺序确定,而不是函数调用的规则.

编辑注释:我的原始答案被误解了f(2, 2).的顺序f,getf()(g(),h(),j()),getf()仍然是不确定的.(谢谢@KABoissonneault,所有评论者.)

然而,(如@Yakk指出),这是非常重要的:即使g,h,j,.then( . . . ),a(b1, b2, b3)是不平凡的表情,他们每个人都完全评估,并绑在各自的功能参数其他的人都开始进行评估之前.标准声明如下:

§5.2.2 - 函数调用5.2.2.4:

...后缀表达式在表达式列表中的每个表达式和任何默认参数之前进行排序.与参数初始化相关联的每个值计算和副作用以及初始化本身在每个值计算和与任何后续参数的初始化相关联的副作用之前被排序.

但是,github草案中缺少其中一个新句子:

与参数初始化相关联的每个值计算和副作用以及初始化本身在每个值计算和与任何后续参数的初始化相关联的副作用之前被排序.

这个例子在那里.它解决了几十年前的问题(正如Herb Sutter所解释的那样)具有异常安全性

f(std::unique_ptr<A> a, std::unique_ptr<B> b);

f(get_raw_a(),get_raw_a()); 
Run Code Online (Sandbox Code Playgroud)

如果其中一个调用b1在另一个原始指针与其智能指针参数绑定之前抛出,则会泄漏. 编辑:正如TC指出的那样,示例存在缺陷,因为来自原始指针的unique_ptr构造是显式的,因此无法编译.

还要注意这个经典问题(标记为C,而不是C++):

int x=0;
x++ + ++x;
Run Code Online (Sandbox Code Playgroud)

仍未定义.

  • @JohanLundberg本文还有另一件事我认为很重要.`a(b1()(),b2()())`可以按任何顺序排序`b1()()`和`b2()()`但它*不能*做`b1()`然后` b2()()`然后`b1()()`:它可能不再交错执行它们.简而言之,"8.功能调用的替代评估令"是批准的变更的一部分. (2认同)
  • `f(i ++,i)`未定义.它现在没有具体说明.Stroustrup的字符串示例可能未指定,未定义.`f(get_raw_a(),get_raw_a());`不会编译,因为相关的`unique_ptr`构造函数是显式的.最后,`x ++ + ++ x`是未定义的,句点. (2认同)

Bar*_*rry 36

C++中禁止交错17

在C++ 14中,以下内容不安全:

void foo(std::unique_ptr<A>, std::unique_ptr<B> );

foo(std::unique_ptr<A>(new A), std::unique_ptr<B>(new B));
Run Code Online (Sandbox Code Playgroud)

在函数调用期间,此处有四个操作

  1. new A
  2. unique_ptr<A> 构造函数
  3. new B
  4. unique_ptr<B> 构造函数

这些的排序是完全未指定的,因此完全有效的排序是(1),(3),(2),(4).如果选择了这个顺序并且(3)抛出,那么来自(1)的内存泄漏 - 我们还没有运行(2),这将防止泄漏.


在C++ 17中,新规则禁止交错.来自[intro.execution]:

对于每个函数调用F,对于在F内发生的每个评估A以及在F内未发生但在同一线程上作为同一信号处理程序(如果有)的一部分进行评估的每个评估B,A在B之前被排序或者B在A之前排序.

这句话的脚注如下:

换句话说,函数执行不会相互交错.

这给我们留下了两个有效的排序:(1),(2),(3),(4)或(3),(4),(1),(2).没有说明采取哪种排序,但这两种都是安全的.现在禁止在(2)和(4)之前发生(1)(3)的所有排序.

  • 我想知道这种变化如何影响优化。编译器现在大大减少了如何组合和交错与参数计算相关的 CPU 指令的选项数量,因此可能会导致 CPU 利用率降低? (3认同)