C ++ 17引入的评估顺序保证是什么? [英] What are the evaluation order guarantees introduced by C++17?

查看:185
本文介绍了C ++ 17引入的评估顺序保证是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

投票人在 C ++ 17评估中的影响典型C ++代码的顺序保证(P0145R3)? (编辑:更新指向最新版本的链接r3



  i = 1; 
f(i ++,i)


$ b b

  std :: cout< f()< f()< F() ; 

  f(g(),h(),j()); 


解决方案

未指定,通过 C ++ 17 指定并有效。



<$ p


$ b

p> i = 1;
f(i ++,i)


现在尚未指定。


  std :: cout< f()< f()< F() ; 


未指定,但会与运算符优先级兼容,对 f 的第一次评估将首先在流中。 (例如下面)。


  f(g ; 


仍有未指定的g,h,j的评估顺序。注意 getf()(g(),h(),j()),规则规定 getf() g,h,j 之前将对进行评估。



还要注意提案文本中的以下示例:


  std :: string s =但我听说它的工作,即使你不相信它
s.replace(0,4,).replace(s.find(even),4,only)
.replace(s.find 't),6,);


示例来自 C ++编程语言版本,Stroustrup ,并且曾经是未指定的行为,但是使用C ++ 17它将按预期工作。还有类似的问题与可恢复的函数( .then(。。))。



以下内容:

  #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&话;
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()?\\\
:,);
++ i;
return word;
//仍然不可能与C ++ 17:
// return words [i ++] +(i == words.size()?\\\
:,);

}
};

int main(){
auto spk = Speaker {{All,Work,and,no,play}}
std :: cout<< spk()< spk()< spk()< spk()< spk();
}

使用C ++ 14和我们之前

  play 
no,and,Work,All,

而不是

  ,play 

请注意,上述效果与

相同

 ((((std :: cout << spk())<<<<< spk())& < spk())<< spk()); 

但是,在C ++ 17之前,不能保证第一个调用会先进入流。



参考资料:从接受建议


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



赋值表达式从右到左计算。这个
包括复合赋值。



操作符的操作数从左到右计算。在
摘要中,以a,然后
b,然后c,然后d的顺序计算以下表达式:


  1. ab

  2. a-> b

  3. a - > * b

  4. a(b1,b2, b3)

  5. b @ = a

  6. a [b]

  7. a< b

  8. a >> b

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


编辑备注:我的原始答案被误解 a(b1,b2,b3) 。仍然未指定 b1 b2 b3 的顺序。 (感谢@KABoissonneault,所有评论者。)



但是,(如@Yakk指出),这很重要:即使 b1 b2 b3 是非平凡表达式,并绑定到相应的函数参数之前,其他的开始进行评估。标准说明如下:


§5.2.2 - 函数调用5.2.2.4:



。 。 。
postfix-expression在
表达式列表中的每个表达式和任何默认参数之前排序。与每个参数初始化相关联的每个值计算和
副作用以及
初始化本身在每个值计算之前被排序,并且
副作用与任何后续的$ b $的初始化相关联b参数。


但是,这些新句子中有一个从 github draft


与参数的
初始化相关联的每个值计算和副作用以及初始化本身是
,在每个值计算和副作用相关联之前,对任何后续参数的初始化进行关联


示例。它解决了数十年的问题(正如Herb Sutter所解释的那样)与异常安全之类的

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

f(get_raw_a(),get_raw_a());如果其中一个调用 get_raw_a()

c>会抛出之前的
raw指针绑定到它的智能指针参数。
edit:正如T.C.该示例是有缺陷的,因为unique_ptr从原始指针的构造是显式的,阻止这种编译。



还要注意这个古典查询(标记为 C ,而不是 C ++ ):


  int x = 0; 
x ++ + ++ x;


还未定义。


What are the implications of the voted in C++17 evaluation order guarantees (P0145R3) on typical C++ code? (edit: updated the link to the latest version, r3)

What does it change about things like

i=1;
f(i++, i)

and

std::cout << f() << f() << f() ;

or

f(g(),h(),j());

解决方案

Some common cases where the evaluation order has so far been unspecified, are specified and valid with C++17. Some undefined behaviour is now instead unspecified.

What about things like

i=1;
f(i++, i)

was undefined but is now unspecified.

std::cout << f() << f() << f() ;

Was unspecified, but will become compatible with operator precedence so that the first evaluation of f will come first in the stream. (examples below).

f(g(),h(),j());

still has unspecified evaluation order of g, h, j. Note that getf()(g(),h(),j()), the rules state that getf() will be evaluated before g,h,j.

Also note the following example from the proposal text:

 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, "");

The example comes from The C++ Programming Language, 4th edition, Stroustrup, and used to be unspecified behaviour, but with C++17 it will work as expected. There were similar issues with resumable functions (.then( . . . )).

As another example, consider the following:

#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() ;
}

With C++14 and before we may (and will) get results such as

play
no,and,Work,All,

instead of

All,work,and,no,play

Note that the above is in effect the same as

(((((std::cout << spk()) << spk()) << spk()) << spk()) << spk()) ;

But still, before C++17 there was no guarantee that the first calls would come first into the stream.

References: From the accepted proposal:

Postfix expressions are evaluated from left to right. This includes functions calls and member selection expressions.

Assignment expressions are evaluated from right to left. This includes compound assignments.

Operands to shift operators are evaluated from left to right. In summary, the following expressions are evaluated in the order a, then b, then c, then d:

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

Furthermore, we suggest the following additional rule: the order of evaluation of an expression involving an overloaded operator is determined by the order associated with the corresponding built-in operator, not the rules for function calls.

Edit note: My original answer misinterpreted a(b1, b2, b3). The order of b1, b2, b3 is still unspecified. (thank you @KABoissonneault, all commenters.)

However, (as @Yakk points out) and this is important: Even when b1, b2, b3 are non-trivial expressions, each of them are completely evaluated and tied to the respective function parameter before the other ones are started to be evaluated. The standard states this like this:

§5.2.2 - Function call 5.2.2.4:

. . . The postfix-expression is sequenced before each expression in the expression-list and any default argument. Every value computation and side effect associated with the initialization of a parameter, and the initialization itself, is sequenced before every value computation and side effect associated with the initialization of any subsequent parameter.

However, one of these new sentences are missing from the github draft:

Every value computation and side effect associated with the initialization of a parameter, and the initialization itself, is sequenced before every value computation and side effect associated with the initialization of any subsequent parameter.

The example is there. It solves a decades-old problems (As explained by Herb Sutter) with exception safety where things like

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

f(get_raw_a(),get_raw_a()); 

would leak if one of the calls get_raw_a() would throw before the other raw pointer was tied to it's smart pointer parameter. edit: as pointed out by T.C. the example is flawed since unique_ptr construction from raw pointer is explicit, preventing this from compiling.

Also note this classical question (tagged C, not C++):

int x=0;
x++ + ++x;

is still undefined.

这篇关于C ++ 17引入的评估顺序保证是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆