Discussion
Team LiB
Previous Section Next Section

Discussion

In the early days of C, processor registers were a precious resource, and compilers were hard pressed to allocate them efficiently for complex expressions in high-level languages. To allow generation of faster code, the creators of C gave the register allocator an extra degree of freedom: When calling a function, the order of evaluation of its arguments was left unspecified. That motivation is arguably less strong with today's processors, but the fact remains that the order of evaluation is unspecified in C++ and, as it turns out, varies widely across compilers. (See also Item 30.)

This can cause big trouble to the unwary. Consider this code:



void Transmogrify( int, int );





int count = 5;


Transmogrify( ++count, ++count );        // order of evaluation unknown



All we can say for certain is that count will be 7 as soon as TRansmogrify's body is enteredbut we can't say which of its arguments is 6 and which is 7. This uncertainty applies to much less obvious cases, such as functions that modify their argument (or some global state) as a side effect:



int Bump( int& x ) {return ++x; }


Transmogrify( Bump(count), Bump(count) );  // still unknown



Per Item 10, avoid global and shared variables in the first place. But even if you avoid them, others' code might not. For example, some standard functions do have side effects (e.g., strtok, and the various overloads of operator<< that take an ostream).

The cure is simpleuse named objects to enforce order of evaluation. (See Item 13.)



int bumped = ++count;


Transmogrify( bumped, ++count );         // ok



    Team LiB
    Previous Section Next Section