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. 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. 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, siz开发者_如何转开发e_type is int
in debugger backtrace stack looks like this:
- i = 1 upon entry to operator() //okay
- i = 0 after finish from by_index //okay
- i = 1 upon entry to matrix:: operator() //not right, should be 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.
Which fits the bill exactly. The values of i and j may be evaluated before the call to by_index(), or after it. 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.
Finally I found where in the standard this is specified (n1905 draft):
(5.2.2-8) - The order of evaluation of arguments is unspecified. 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.
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.
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.
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.
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.
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"). 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.
精彩评论