Discussion
Team LiB
Previous Section Next Section

Discussion

The primary reason not to overload operator&&, operator|| , or operator, (comma) is that you cannot implement the full semantics of the built-in operators in these three cases, and programmers commonly expect those semantics. In particular, the built-in versions evaluate left-to-right, and for && and || also use short-circuit evaluation.

The built-in versions of && and || first evaluate their left-hand expression, and if that fully determines the result (false for &&, true for ||) then the right-hand expression doesn't need to be evaluatedand is guaranteed not to be. We all get so used to this handy feature that we routinely allow the correctness of the right-hand side depend on the success of the left-hand side:



Employee* e = TryToGetEmployee();


if( e && e->Manager() )


  // …



This code's correctness relies on the fact that e->Manager() will not be evaluated if e is null. This is perfectly usual and fineunless the && used is an overloaded operator&&, because then the expression involving && will follow function rules instead:

  • Function calls always evaluate all arguments before execution.

  • The order of evaluation of function arguments is unspecified. (See also Item 31.)

So let's look at a modernized version of the snippet above that uses smart pointers:



some_smart_ptr<Employee> e = TryToGetEmployee();


if( e && e->Manager() )


  // …



Now, say this code happens to invoke an overloaded operator&& (provided by the author either of some_smart_ptr or of Employee). Then the code will still look fine to the reader, but will potentially (and disastrously) call e->Manager() when e is null.

Some other code won't dump core even in the presence of such eager evaluation, but becomes incorrect for a different reason if it depends on the order in which the two expressions are evaluated. The effects, of course, can be just as harmful. Consider:



if( DisplayPrompt() && GetLine() )


  // …



If operator&& is a user-defined operator, it is unspecified whether DisplayPrompt or GetLine is called first. The program could inadvertently end up waiting for input from the user before displaying the explanatory prompt.

Of course, such code may seem to work with your current compiler and build settings. It's still fragile. Compilers can (and do) choose whatever order they find fit best for any particular call, taking into account concerns such as generated code size, available registers, expression complexity, and so on. So the same call might behave differently depending on the compiler version, the compiler switch settings, and even on the statements surrounding the call.

The same fragility occurs with the comma operator. Like && and ||, the built-in comma guarantees that its expressions will be evaluated left-to-right (unlike && and ||, it always evaluates both). A user-defined comma operator cannot guarantee left-to-right evaluation, usually with surprising results. For example, if the following code invokes a user-defined comma operator, it is unspecified which of f or g receives the value 0 and which receives the value 1.



int i = 0;


f( i++ ), g( i++ );                      // see also Item 31



    Team LiB
    Previous Section Next Section