C ++ expression

I ran into a curious problem evaluating 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 thought that the matrix (i, j) would be evaluated after calling buy_index, so that i, j would be updated. this seems correct, i checked in the debugger. however, for some types of matrices, in particular those that should be distinguished by size_type something else, for example int, the update in by_index is lost. changing the code fixes the problem a bit:

 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 statement fails? thanks

prototypes that work and which

 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 

on the backdrace stack of the debugger is as follows:

  • i = 1 when entering the operator () // okay
  • i = 0 after completion with by_index // okay
  • i = 1 when entering into the matrix :: operator () // not correct, should be 0
+4
source share
5 answers

In my opinion, it comes down to the order of evaluation.

The standard says -

(5.4) Except where noted, the procedure for evaluating the operands of individual operators and subexpressions of individual expressions and the order in which side effects occur are not defined.

Which exactly matches the score. The values ​​of i and j can be evaluated before calling by_index () or after it. You cannot say it is unspecified.

I will add that the form that solves your problem is far more readable in my eyes, and I would use it regardless of the correctness of the first form ...

+3
source

I suspect that a link to another type violates the strict alias rules that your compiler uses to optimize more efficiently. You have two variables / references of different types, and the compiler assumes that they do not refer to the same memory (but in fact they do). The compiler then optimizes the code under this incorrect assumption, which leads to incorrect results.

You can try to compile with -fno-strict-aliasing (or the equivalent) to disable these optimizations and see if it improves the situation.

+2
source

Finally, I found where the standard states this (draft n1905):

(5.2.2-8). The order in which arguments are evaluated is not defined. All side effects of argument expression evaluations take effect before the function is entered. The evaluation order of the post fix expression and the list of argument expressions are undefined.

The specified postfix expression is the part to the left of () . Thus, an external function call is not specified if by_index(i, j, index) or its arguments (i, j) are evaluated first.

After the function returns, a sequence point appears, therefore, when by_index(i, j, index) returns all side effects, but the parameters (i, j) can already be evaluated (and the values ​​were stored in the register or sth.) Before this function is even called called up.

+2
source

Passing data through argument lists, like this, is very, very, unclear. Relying on implicit castings between reference types on bases of various sizes is very, very dangerous.

In any case, if you call by_index(size_t &,… with an int argument, you take a temporary reference. This should be a warning, but you may be in the older compiler. Try casting explicitly.

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

But, of course, it is not guaranteed that you need to do the right thing. Indeed, you need to parse unsigned from the signed one and not assume sizeof(int) == sizeof(size_t) , because often this is not the case.

0
source

As a side note, this is a typical case where a brief overview of clarity is something Brian Kernigan strongly advises us to avoid (he wrote an excellent book on these issues, "Programming Practices"). In this code, the evaluation order is not defined, which leads to a “side effect” of unpredictable results. The change you made is the recommended approach to situations like this.

0
source

Source: https://habr.com/ru/post/1298957/


All Articles