The standard defines a few exceptions where evaluation order is specified:
· Operands of the binary logical operators (&& and ||) are evaluated left-to-right, and short-circuited.
· Operands of the comma operator (,) are evaluated left-to-right.
· The first operand of the conditional operator (?:) is completely evaluated before continuing. Only one of the second and third expressions is evaluated.
· Initialization of class bases and members is evaluated in a strict order, so to guarantee destruction in the reverse order. Direct base classes are initialized first, in the order they appear in the base-specifier-list. Then, non-static members are initialized in the order they were declared in the class definition.
When operators are overloaded, the semantics became those of function-call expressions. So the evaluation order for overloaded binary logical operators and the overloaded comma operator is again unspecified. This means special care must be taken when working with templates, where the concrete type is unknown. The evaluation order for expressions including those operators is dependant on whether the provided type overloads such operators; and for binary logical operators it also means that short-circuited evaluation is not guaranteed. This is why overloading them is sometimes considered rude.
Assuming a type overloads operators with their usual meaning, some protection can be achieved by wrapping them in a type for which they are not:
template< typename T >Considering an evil_int type that behaves like an integer and overloads every operator with their usual meaning,
struct protected_
{
protected_( T value ) : _value( value ) {}
T value() const { return _value; }
operator unspecified-bool-type() const { return _value; }
T _value;
};
template< typename T >
protected_< T& > protect( T& v ){ return v; }
evil_int value1 = 0, value2 = 0;results in "0, true" (for a particular C++ implementation); while
std::cout
<< ( ++value1, value1++ ) << ", "
<< ( value2 && ++value2 ) << std::endl;
evil_int value1 = 0, value2 = 0;results in "1, false" as expected for an integral-like type.
std::cout
<< ( protect( ++value1 ), protect( value1++ ) ).value()
<< ", "
<< ( protect( value2 ) && protect( ++value2 ) )
<< std::endl;
No comments:
Post a Comment