|
From: Ben Voigt [C++ MVP] on 8 May 2008 14:27 Alf P. Steinbach wrote: > * Ben Voigt [C++ MVP]: >>>> You might not see that example as typed but if( a * b = c) ... does >>>> occure because of typing mistakes. The compiler will catch this. >>> And why would you want to prevent that? >> >> Because the side-effect of the assignment operator is to change a >> temporary that is immediately thrown away, obviously not intended. >> >> The programmer meant to use the comparison operator == instead. > > Did she? > > Anyway, the techniques for dealing with > inadvertent-assignment-in-condition for built-in types (namely high > warning level for compilation, writing constants on left side, code > inspection, systematic testing, etc.) apply here also. "writing constants on left side"? That's exactly what is going on here! > > Fixing that possible problem for e.g. class Rational amounts to > nothing compared to the rest of code, and as mentioned it does have a > cost, so, pain but no gain. > > Cheers, > > - Alf
From: Alf P. Steinbach on 8 May 2008 16:09 * Ben Voigt [C++ MVP]: > Alf P. Steinbach wrote: >> * Ben Voigt [C++ MVP]: >>>>> You might not see that example as typed but if( a * b = c) ... does >>>>> occure because of typing mistakes. The compiler will catch this. >>>> And why would you want to prevent that? >>> Because the side-effect of the assignment operator is to change a >>> temporary that is immediately thrown away, obviously not intended. >>> >>> The programmer meant to use the comparison operator == instead. >> Did she? >> >> Anyway, the techniques for dealing with >> inadvertent-assignment-in-condition for built-in types (namely high >> warning level for compilation, writing constants on left side, code >> inspection, systematic testing, etc.) apply here also. > > "writing constants on left side"? > > That's exactly what is going on here! Nope. An expression is not necessarily a constant, and in particular, in C++, an rvalue is not necessarily a constant. For class types it is in most cases not constant, e.g. the std::vector<T>().swap(v) idiom for clearing a vector v relies on this, and the rvalue non-constness is also used in the code below. Applying my well-known telepathic powers, I think you're perhaps confusing an expectation about the result of '*' in e.g. mathematics, with operator results in C++. The technique of using constants on the lhs has two common cases: literal, or something named and declared 'const', which for the above and a class Rational would mean Rational const product = a*b; if( product = c ) // Oops. Unfortunately, in this context, operator= is one of those that can't be defined as a free-standing function. If it could then the above would not even be potential problem, because a temporary can't be bound to reference to non-const. With member operator= you can't directly prohibit assignment to non-const rvalue (which is generally preferable, especially for optimization). An alternative is to change the syntax for assignment, so that '=' simply isn't valid. Examples: 'a.value() = b', or 'a.assignFrom( b )', or 'a ^= b'. The '^=' syntax on the assumption that exclusive-or assignment isn't useful on its own for this class. Example that illustrates all three assignment syntaxes mentioned above (I would not, however, do this, because I don't think =/== is a real problem): <code> template< typename T > class AssignableRef { private: T* myRef; public: AssignableRef( T& v ): myRef( & v ) {} T& operator=( T const& v ) { return myRef->assignFrom( v ); } }; class Rational { private: Rational& operator=( Rational const& ); // No such. public: Rational( int = 0 ) {} void swap( Rational& ) {} Rational& assignFrom( Rational const other ) { Rational( other ).swap( *this ); return *this; } Rational& operator^=( Rational const& other ) { return assignFrom( other ); } AssignableRef<Rational> value() { return AssignableRef<Rational>( *this ); } Rational& operator*=( Rational const& ) { return *this; } bool operator==( Rational const& ) { return true; } }; Rational operator*( Rational const& a, Rational const& b ) { return Rational( a ) *= b; } int main() { Rational a, b, c; a ^= b; // OK, this is an assignment. a.assignFrom( b ); // OK, this is also an assignment. a.value() = b; // OK. this is also an assignment. if( a*b == c ) {} // OK, this is a comparision. //if( a*b = c ) {} // Oops, does not compile (which is OK). } </code> The question is though, is the potential error detection really worth the non-standard notation? If it isn't, then that means the potential error isn't really that much of a problem in practice. And then the cost of a 'const' result, in particular prohibiting optimization, is counter-indicated. There are also other alternatives, such as the extremely dirty trick (formally UB if you use the standard library) of redefining 'if' so that it /requires/ a 'bool' value. But that extremely dirty trick doesn't help with ?:, nor does it help with assignment to bool... Another alternative: it might be that the compiler can produce a warning that can be turned into an error, but this alternative is compiler-specific. A third is, as mentioned, the convention always writing something const on the left hand side of '==', although the logic of that is questionable, for what if you forget, how can you detect that? Best: simply don't write '=' instead of '==', and test the software so that any such slip-ups are detected. If '=' instead of '==' passes all tests, then, after all, it doesn't matter. >> Fixing that possible problem for e.g. class Rational amounts to >> nothing compared to the rest of code, and as mentioned it does have a >> cost, so, pain but no gain. Cheers, & hth., - Alf -- A: Because it messes up the order in which people normally read text. Q: Why is it such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail?
From: Ben Voigt [C++ MVP] on 8 May 2008 16:47 Alf P. Steinbach wrote: > * Ben Voigt [C++ MVP]: >> Alf P. Steinbach wrote: >>> * Ben Voigt [C++ MVP]: >>>>>> You might not see that example as typed but if( a * b = c) ... >>>>>> does occure because of typing mistakes. The compiler will catch >>>>>> this. >>>>> And why would you want to prevent that? >>>> Because the side-effect of the assignment operator is to change a >>>> temporary that is immediately thrown away, obviously not intended. >>>> >>>> The programmer meant to use the comparison operator == instead. >>> Did she? >>> >>> Anyway, the techniques for dealing with >>> inadvertent-assignment-in-condition for built-in types (namely high >>> warning level for compilation, writing constants on left side, code >>> inspection, systematic testing, etc.) apply here also. >> >> "writing constants on left side"? >> >> That's exactly what is going on here! > > Nope. An expression is not necessarily a constant, and in The OP had declared the return type of his operator as const in order to make such expressions const. > particular, in C++, an rvalue is not necessarily a constant. For > class types it is in most cases not constant, e.g. the > std::vector<T>().swap(v) idiom for clearing a vector v relies on > this, and the rvalue non-constness is also used in the code below.
From: Alf P. Steinbach on 8 May 2008 17:27 * Ben Voigt [C++ MVP]: > Alf P. Steinbach wrote: >> * Ben Voigt [C++ MVP]: >>> Alf P. Steinbach wrote: >>>> * Ben Voigt [C++ MVP]: >>>>>>> You might not see that example as typed but if( a * b = c) ... >>>>>>> does occure because of typing mistakes. The compiler will catch >>>>>>> this. >>>>>> And why would you want to prevent that? >>>>> Because the side-effect of the assignment operator is to change a >>>>> temporary that is immediately thrown away, obviously not intended. >>>>> >>>>> The programmer meant to use the comparison operator == instead. >>>> Did she? >>>> >>>> Anyway, the techniques for dealing with >>>> inadvertent-assignment-in-condition for built-in types (namely high >>>> warning level for compilation, writing constants on left side, code >>>> inspection, systematic testing, etc.) apply here also. >>> "writing constants on left side"? >>> >>> That's exactly what is going on here! >> Nope. An expression is not necessarily a constant, and in > > The OP had declared the return type of his operator as const in order to > make such expressions const. Perhaps I should rephrase that for clarity then: the techniques that client code programmers intentionally use for dealing with... That's not what's going on above. The client code programmer who writes "if( a*b = c ) ..." is not intentionally writing a constant expression on the left hand side, because that expression could very well and would normally be non-constant for class type a, b and c. As already illustrated, the ensure-constant-lhs technique can be applied to the above example, and the point you reacted to was the statement that it can. And then the code looks different. :-) >> particular, in C++, an rvalue is not necessarily a constant. For >> class types it is in most cases not constant, e.g. the >> std::vector<T>().swap(v) idiom for clearing a vector v relies on >> this, and the rvalue non-constness is also used in the code below. Cheers, & hth., - Alf -- A: Because it messes up the order in which people normally read text. Q: Why is it such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail?
From: Ben Voigt [C++ MVP] on 8 May 2008 18:31
Alf P. Steinbach wrote: > * Ben Voigt [C++ MVP]: >> Alf P. Steinbach wrote: >>> * Ben Voigt [C++ MVP]: >>>> Alf P. Steinbach wrote: >>>>> * Ben Voigt [C++ MVP]: >>>>>>>> You might not see that example as typed but if( a * b = c) ... >>>>>>>> does occure because of typing mistakes. The compiler will >>>>>>>> catch this. >>>>>>> And why would you want to prevent that? >>>>>> Because the side-effect of the assignment operator is to change a >>>>>> temporary that is immediately thrown away, obviously not >>>>>> intended. The programmer meant to use the comparison operator == >>>>>> instead. >>>>> Did she? >>>>> >>>>> Anyway, the techniques for dealing with >>>>> inadvertent-assignment-in-condition for built-in types (namely >>>>> high warning level for compilation, writing constants on left >>>>> side, code inspection, systematic testing, etc.) apply here also. >>>> "writing constants on left side"? >>>> >>>> That's exactly what is going on here! >>> Nope. An expression is not necessarily a constant, and in >> >> The OP had declared the return type of his operator as const in >> order to make such expressions const. > > Perhaps I should rephrase that for clarity then: the techniques that > client code programmers intentionally use for dealing with... > > That's not what's going on above. > > The client code programmer who writes "if( a*b = c ) ..." is not > intentionally writing a constant expression on the left hand side, > because that expression could very well and would normally be > non-constant for class type a, b and c. > As already illustrated, the ensure-constant-lhs technique can be > applied to the above example, and the point you reacted to was the > statement that it can. No it cannot, unless you make assumptions about mathematical equalities holding for arbitrary class types, the same ones you accused me of several messages ago. At least I'm thinking of transforming that into if (0 == a * b - c) But that's not necessarily valid for all types. Some types may not even define operator -. > > And then the code looks different. :-) > > >>> particular, in C++, an rvalue is not necessarily a constant. For >>> class types it is in most cases not constant, e.g. the >>> std::vector<T>().swap(v) idiom for clearing a vector v relies on >>> this, and the rvalue non-constness is also used in the code below. > > Cheers, & hth., > > - Alf |