For user-defined types, it is wise to emulate the functionality of built-in types where possible (this especially applies to types that can represent numbers). For operator -, consider the following:
tvector3 v1( 1, 2, 3 ), v2( 4, 5, 6 ), v3( v1-v2 ); ... if( v1-v2 = v3 ) // oops, meant to do a comparison (==) ...
The difference is subtle here, and most compilers will flag the conditional on tvector3 because of the (incorrect) attempted conversion to bool. For built-in types, this code of course would not compile. But say you have a rational number class, for instance, you'd probably want an implicit conversion to float, which as a built-in type has an implicit conversion to int, which has an implicit conversion to bool. In this case the above typo would compile and give you extremely erroneous results.
To solve this problem at compile-time, not only will you return an object by value from operator-(), but you will also make that return value const:
const rational operator -( const rational & );
This prevents debugging headaches by not allowing the "I meant conditional instead of assignnemt" to compile. This applies to all types implicitly convertible to int or bool, but again for consistency's sake, it's a good habit to get into.