简体   繁体   English

C ++表达式评估顺序

[英]C++ expression evaluation order

i ran into a curious problem regarding evaluation of expressions: 我遇到了一个关于表达式评估的奇怪问题:

reference operator()(size_type i, size_type j) {
  return by_index(i, j, index)(i, j); // return matrix index reference with changed i, j
}

matrix& by_index(size_type &i, size_type &j, index_vector &index) {
  size_type a = position(i, index); // find position of i using std::upper_bound
  size_type b = position(j, index);
  i -= index[a];
  j -= index[b];
  return matrix_(a,b); // returns matrix reference stored in 2-D array
}

I have thought matrix(i,j) will be evaluated after the call to buy_index, so that i, j will be updated. 我认为在调用buy_index之后将对matrix(i,j)进行评估,以便更新i,j。 this appears to be correct, i verified in debugger. 这似乎是正确的,我在调试器中验证。 however, for some types of matrix, specifically those which have to cast size_type the something else, for example int, the update in by_index is lost. 但是,对于某些类型的矩阵,特别是那些必须将size_type转换为其他类型的矩阵,例如int,by_index中的更新将丢失。 modifying code slightly removes the problem: 修改代码稍微消除了问题:

reference operator()(size_type i, size_type j) {
  matrix &m = by_index(i, j, index);
  return m(i, j); 
}

do you know why the first operator misbehaves? 你知道为什么第一个操作员行为不端吗? thanks 谢谢

prototypes which work and which do not 原型有效,哪些无效

inline reference operator () (size_t i, size_t j); // ublas, size_type is std::size_t
reference operator () (int i, int j); // other prototype, size_type is int

in debugger backtrace stack looks like this: 在调试器中,backtrace堆栈如下所示:

  • i = 1 upon entry to operator() //okay 进入operator()后i = 1 //好吧
  • i = 0 after finish from by_index //okay 从by_index完成后i = 0 //好的
  • i = 1 upon entry to matrix:: operator() //not right, should be 0 进入matrix :: operator()//不正确时i = 1,应为0

In my opinion, this boils down to order of evaluation. 在我看来,这归结为评估顺序。

The standard says - 标准说 -

(5.4) Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified. (5.4)除非另有说明,否则单个操作员的操作数和个别表达式的子表达式的评估顺序以及副作用发生的顺序是未指定的。

Which fits the bill exactly. 这完全符合法案。 The values of i and j may be evaluated before the call to by_index(), or after it. 可以在调用by_index()之前或之后评估i和j的值。 You can't tell - this is unspecified. 你无法分辨 - 这是未指明的。

I will add that the form that solves your problem is far more readable in my eyes, and I would have used it regardless of correctness of the first form... 我补充一点解决您的问题的形式表现在我眼中更具可读性,我会不顾第一种形式的正确性,使用它...

I suspect that casting a reference to a different type breaks strict aliasing rules that your compiler uses to optimize more efficiently. 我怀疑对不同类型的引用是否会破坏编译器用来更有效地优化的严格别名规则 You have two variables/references of different type and the compiler assumes that they don't refer to the same memory (but which they in fact do). 你有两个不同类型的变量/引用,并且编译器假定它们不引用相同的内存(但它们实际上是这样做的)。 The compiler then optimizes the code under that wrong assumption which produces wrong results. 然后,编译器在错误的假设下优化代码,从而产生错误的结果。

You can try to compile with -fno-strict-aliasing (or equivalent) to disable these optimizations and see if it improves the situation. 您可以尝试使用-fno-strict-aliasing (或等效项)进行编译以禁用这些优化,并查看它是否改善了这种情况。

Finally I found where in the standard this is specified (n1905 draft): 最后我发现标准中指定的位置(n1905草案):

(5.2.2-8) - The order of evaluation of arguments is unspecified. (5.2.2-8) - 参数的评估顺序是未指定的。 All side effects of argument expression evaluations take effect before the function is entered. 参数表达式求值的所有副作用在输入函数之前生效。 The order of evaluation of the postfix expression and the argument expression list is unspecified. post fi x表达式和参数表达式列表的评估顺序未指定。

The postfix expression mentioned is the part to the left of () . 提到的后缀表达式()左侧的部分。 So in the "outer" function call it is not specified if by_index(i, j, index) or it's arguments (i, j) are evaluated first. 因此,在“外部”函数调用中,如果by_index(i, j, index)或它的参数(i, j) by_index(i, j, index)则不指定它。

There is a sequence point after a function returns, so when by_index(i, j, index) returns all side effects are complete, but the (i, j) parameters might already have been evaluated (and the values been stored in a register or sth.) before that function even go called. 函数返回后有一个序列点,所以当by_index(i, j, index)返回时,所有副作用都已完成,但(i, j)参数可能已经被评估过(并且值已存储在寄存器或......那个功能之前甚至会被召唤。

Passing data through argument lists like that is very, very, unclear. 通过这样的参数列表传递数据是非常非常不清楚的。 Relying on implicit casts between reference types to different-sized bases is very, very unsafe. 依赖于引用类型与不同大小的基础之间的隐式转换是非常非常不安全的。

Anyway, if you're calling by_index(size_t &,… with an int argument, you're taking the reference of a temporary. This should be a warning, but maybe you're on an older compiler. Try an explicit cast. 无论如何,如果你正在调用by_index(size_t &,…带有一个int参数,你就是一个临时的引用。这应该是一个警告,但也许你是在一个较旧的编译器。尝试一个显式的强制转换。

reinterpret_cast< size_t & >( i ) /* modify i before next fn call */

But of course that's not guaranteed to do the right thing. 但当然,这并不能保证做正确的事情。 Really, you need to sort out unsigned from signed and not assume sizeof(int) == sizeof(size_t) because often it isn't. 实际上,你需要从signed中对unsigned进行整理,而不是假设sizeof(int) == sizeof(size_t)因为它通常不是。

As an additional remark, this is the typical case where been concise overrules clarity, something Brian Kernighan strongly advises us to avoid (He wrote an excellent book on these matters, "The Practice of Programming"). 作为一个额外的评论,这是典型的案例,简明扼要清晰,Brian Kernighan强烈建议我们避免(他写了一本关于这些问题的优秀书籍,“编程实践”)。 The evaluation order is not well defined in such code, what leads to the "side effect" of unpredictable results. 评估顺序在这样的代码中没有很好地定义,导致不可预测结果的“副作用”。 The change you have made is the recommended approach to situations like this one. 您所做的更改是针对此类情况的推荐方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM