Topic: Explicit conversion operators NEEDED BADLY


Author: sean@delta.com (Sean L. Palmer)
Date: 1996/10/11
Raw View

> I don't want to define an implicit conversion from Rational to double
> and it is not reasonable to ask me to specialize f() for every type that
> is castable to double (especially since some such types may not be
> written yet). So there is no way to write f() such that it works for
> build-in types, implicitly castable classes, and classes with
> to_double() functions. Worse, if I use a 3rd-party class that supplies a
> conversion function called asDouble() instead of to_double(), my
> template becomes totally useless.

Hear! Hear!  Exactly my point.  Nobody has listened to me up til now.

Anyone writing a fixed-point class will run into these problems.  I'm sure
there are many other classes which will benefit from explicit conversion
operators as well.

> Allowing explicit conversion operators provides a convention for naming
> explicit conversion functions which works for both built-in and
> user-defined types. It is also orthogonal to explicit constructors and
> makes it easier to teach C++.

Agreed.  The exact same reasons that they made explicit constructors, that
same reasoning can be applied to explicit conversion operators.  To provide
a way to make sure the user of a class knows what they are doing with it,
to decrease ambiguity problems, to safeguard conversions that aren't safe,
etc etc.

> Principle: When considering work-arounds for lack of a language feature
> (e.g. to_double() is a work around for the lack of explicit operator
> double()), consider whether the work-around will work in a template
> class or function.

No kidding. Trying to make a template which simulates the behavior of float
or int is impossible because you can't provide all specializations of
conversion (there will always be some ambiguity) and if you only provide
one implicit conversion (let's say it's "int"), then you can't do any
conversion at ALL to double because double is a reserved word and it's a
builtin type which can't be extended with a new ctor. The only mechanism
for getting this done is the user-defined conversion operator, but since
the ambiguity problem exists, this is impractical.

To sum:  We NEED explicit user-defined conversion operators to allow our
classes to be treated in a template as if they were a built-in type like
int or double. Make a type that pretends it's a double. Send it to a
template function that was designed for double and float, but didn't have
your class in mind. Even though you emulate double 100%, provide all its
functionality, you can't provide casts to double that will allow conversion
to whatever they are trying to convert to in the template and still provide
conversion to int as well. Thus the template function has to be redesigned
to handle whatever klunky mechanism we had to use to provide the
unambiguous conversion. Then it won't work for somebody else's double-like
class that uses a different mechanism.

The template should be able to cast any T it's given into double like so:

template <class T=double>
  T distance(const T& x,const T& y, const T& x2,const T& y2) {
    return T(sqrt(double((x2-x)*(x2-x)+(y2-y)*(y2-y))));
  }

Now if I make a Fixed-point class Fixed, and provide implicit conversion to
int, I can't provide implicit conversion to double as well. If I provide
implicit conversion to double, I can't provide implicit conversion to int
as well. Both are needed at various times, but what if they decide they
want to send a Fixed to a function that takes a long as parameter?  Then
ambiguity between double and int conversions arises.  Providing an operator
long won't handle unsigned long. I'd have to convert to all of the
following, plus any classes that other people write that pretend they are
numeric types:

char
signed char
unsigned char
int
unsigned int
short int
unsigned short int
long int
unsigned long int
float
double
long double
complex

That's quite a list. Especially when the conversions for all float types
are the same, and the conversions for all int types are the same.  It would
be unwise to even allow implicit conversions to char, but if I didn't then
if someone tried to explicitly CAST to char, they'd get ambiguity from int
and from double conversions which are available.

It just doesn't work.

So I provide implicit conversion to int and provide a friend function
double Double(const Fixed&) to do the conversion, but can't use double as
the name of the function because double is a reserved word. So the above
template will break even though my Fixed class provides all the necessary
operators and conversions.

If I could just have

class Fixed {
   int v;
public:
   ...
   explicit operator int() const { return v >> 16; } //loses precision
   operator double() const { return v / 65536.0; }
   ...
};

I would also provide explicit casts to all the other basic numeric types
such as char. Then they would be available for explicit casts, but not
implicit casts. The only implicit cast route is through operator double and
conversion from double to char should evoke a compiler warning as expected.

Then it would work in most cases as expected. Any conversion I didn't
provide would get automatically converted from double, saving as much
precision as possible. If they really want an int, they can convert
explicitly to int using the normal cast syntax:

  Fixed f(4.5);
  callintfunction(int(f));
  callintfunction(f); //compiler error: explicit conversion needs explicit
cast

or it could convert from Fixed to double and then from double to int--I
don't care. It would still work.  Slightly slow. But if they explicitly
cast to int they get the really fast shift version of the conversion.

But it would work.

Which is more than I can say about my fixed point class as it stands now.

I'm sure the committee is running into these same problems with its complex
class.  Except I imagine they decided to provide conversion operators
to/from ALL builtin numeric types instead, a lame solution. Or they force
you to convert from complex to double first then convert from double to
char or some such, when you explicitly cast complex to char.

This at least needs to be proposed to the committee. I'd do it myself if I
knew how. Except that someone else would I'm sure do a better job of it
than I could.





[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]