|
Boost : |
From: Peter Bindels (dascandy_at_[hidden])
Date: 2006-11-18 14:29:20
> > But the first principle requires that the types be compatible (i.e.
> > there must be one-to-one correspondence between them). To see why, lets
> > assume we can define operator== between int and float, and define it to
> > round to the nearest int when comparing an int to a float.
>
> No, never do that. Mixed type expressions are always coerced to the
> highest of the types involved or to an even higher type. Otherwise, you
> will really get unpleasant surprises. (OK, your code is only an
> illustration, I know).
>
> > You may define operator== to not round but "promote" the int to a float.
> > That will make your equality comparison an equivalence relation. The
> > problem then will shift to rule 3 because you cannot do the same
> > promotion when copying. Consider this:
> >
> > float a=5.1;
> > int b=a;
> > assert(b==a); // fails!
> >
>
> I don't see this as a surprise -- after all, we have performed a lossy
> assignment in between. Type promotion is a well understood operation.
>
> > That is, "a=b" should be defined for exactly the same types for which
> > "a==b" is defined. Therefore, copy should only be defined between
> > compatible images.
> >
>
> I like this rule. So, the fundamental question is: should a = b
> imply a == b?
>
> What are the language gurus saying about this? Niklaus Wirth and Bertand
> Mayer are certainly in favour of the implication, whereas the C/C++
> inventors opted against. In practice, it amounts to two questions:
>
> 1. Will a default implicit conversion be useful enough to tolerate its
> potential for surprises?
> 2. Can one design the system so that customized conversions can be
> conveniently configured, even if deep inside a nested call?
>
> I'd like to hear the opinion of others about this.
I would say that the equality should hold if the comparison is between
equal types. Comparing 5.1f with 5.1f should equal true, where
comparing (float)5.1f with (double)5.1 should not be required to equal
true, but still be allowed to.
The problem is whether you perform an automatical upcast or downcast
of one of the components. In those cases, the user isn't explicitly
made aware of a change of variable and the user could expect stuff to
work. I consider automatic casting ridiculous, since I prefer to
define new arithmetic-style types in C++ which can't be mapped cleanly
back and forth, no matter which mapping you choose. Try mapping a
32-bit int with a (32-bit) float. Comparing those two will never work
properly, since neither is strictly speaking "higher" than the other.
Downcasting and comparing is wrong:
if (5 != 5.1f) { printf("5 equals 5.1"); } would give the unexpected result.
Even upcasting and comparing is wrong, since downcasts can be implied:
float f = 5.1;
if (f != 5.1) { printf("something wrong"); } would print that
something's wrong, which there is. Worse so, depending on the exact
synthesis of output, the compiler could output that they are equal
(due to caching or compile-time equality checking). There's an
implicit downcast and upcast on the float, which is clearly apparent
on the x86 (because it implicitly upcasts and downcasts everything to
long doubles) but the problem itself is on all computers,
principially.
The only solution would be to disallow comparison between unequal
types by default, requiring the user to specify which type of cast to
use. This would fix a bunch of roundoff errors and would require the
user to think about 5 == 5.1 instead of assuming it to be ok.
I'm voting for the impossible, disallow comparison between unequal
types without explicit casts. This can't be done since it would break
about all applications written so far, including a lot of fairly
trivial examples. For any new library I'm going to write that should
handle arbitrary types in wrapper, I'm explicitly not going to add
magic to make implicit casts work.
Regards,
Peter
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk