Topic: Explicit conversion operators unnecessary


Author: sean@delta.com (Sean L. Palmer)
Date: 1996/10/16
Raw View
> Note that I used 'conversion operator' in the singular. Your design needs
> to determine which of all the possible scalar conversions best represents
> your new type, and which you're most likely to use. A fixed-decimal type,
> for example, probably maps best to either one of the floating-point
types,
> or possibly one of the large integers. Given that you can convert
> implicitly to "long double," you can always use a narrowing cast to, say,
> signed short, just as you can cast a long double to a short.

So to convert from fixed-point to int you would force me to pay the runtime
cost of first converting to long double and then converting to int, when
the shortest path from fixed-point to int only needs a shift and an and
masking.

Typical misunderstanding of design goals.

> About the only place where this is a major problem is when you also want
to
> use operator bool as a 'valid' flag in your type. That's a design, flaw,
> however; since converting any type of scalar to bool is already
> well-defined as a narrowing value conversion, you need to provide an
> explicitly-named boolean validation member function.
>
> The hardest decision to make in emulating a scalar is how well to make it
> fit into the existing type system, especially if such conversions 'lose'
> information. I'd say, if you have a sensible scalar-emulator that can be
> represented exactly by one of the existing types, then provide an
> (implicit) conversion operator to the most specific type that will hold
the
> value, and leave it at that! Let the type system itself perform the rest
of
> the conversions. The main place you'll then find trouble are template
> functions which try to do a double-user-conversion from your custom
scalar
> type to a fundamental type to another custom scalar type. Workaround for
> that: either specialize the template or provide a conversion to *that*
> type. Adding a conversion from class to class is much less dangerous than
> adding one to each of the built-ins.

> To sum up: providing conversions to more than one integral type is a
> mistake in C++ as fundamental as calling virtual functions in
constructors
> or misunderstanding the virtual base system. Conversions and overloading
in
> C++ are wonderful, but *keep it to a minimum*! Doing so keeps your code
> smaller, cleaner, and more maintainable. This I have learned from
> experience: if C++ lets you do 10 things in 20 ways, limit yourself to
> using 2 of the features and using them the same way each time. Which 2 of
> the 10 is your personal choice, which is what I like about C++. The fact
> that most people will use 8 of the 10 is one of the things I don't like
> about maintaining (shock, gasp) C++ code.

I just want a conversion to int and one to double. That's only one scalar
type. Still has disastrous effects. Still ambiguous. Still unworkable. C++
may let me do it in 20 ways, but there are no ways to do it which don't
break something else.

> There are two reasons the whole explicit conversion issue is confusing:
> early reports on explicit constructors implied that conversion ops would
> get the treatment too--or at least people inferred that conclusion--thus
> confusing people, and explicit conversion ops aren't really necessary
even
> though they 'seem' to be necessary. Like the "inherited::" issue or
keyword
> parameters, there's already enough ways to accomplish what you need.

NOT. NOT. The workarounds are kludgy and break other things, namely
templates that you DIDN'T WRITE and CAN'T MODIFY to tailor them to your
class.  I know the inherited keyword would be a bad idea. This is
different. The available ways to do what I need to do result in either
unnecessary runtime overhead or template problems.  For a tiny class like
this that's intended to be extremely general purpose and portable, both are
unacceptable solutions.

Explicit conversion ops are a natural corollary to explicit constructors.
Exact same thing in a different direction. A way to limit available
implicit conversions to reduce ambiguity and mistake-making possibilities.
I wouldn't need explicit conversion ops IF and ONLY IF I could provide
user-defined explicit constructors for built-in types or make friend
functions that used built-in type names as function identifiers. There are
far fewer repercussions to adding explicit conversion ops than these other
fixes for a basic language shortcoming.

> Explicit constructors were a necessity for the standard library, but
> explicit conversion ops are not necessary at all. Without prior art,
broken
> code, needs in the standard library, or a well-written proposal for
> explicit conversion ops, there is no good reason to include them in this
> version of the library.

I don't care about the library, I'm talking about a core C++ feature here.

So exactly HOW would I go about making a "well-written" proposal for
explicit conversion ops and getting it into the hands of people that
matter?


I will post my fixed-point class here for you guys if it's ok (mod: please
let me know by e-mail if it's not ok). It's about eighty lines long.  I can
trim it down actually. I will post it and if anyone can find a good
workaround that doesn't require explicit conversion ops, I will accept that
and retract my proposal.  I'll post the requirements along with the code.
If it's possible to do using the current C++ draft standard, then surely
someone here can figure out how.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: phalpern@truffle.ma.ultranet.com (Pablo Halpern)
Date: 1996/10/18
Raw View
bradds@concentric.net (Bradd W. Szonye) wrote:

>Sean L. Palmer <sean@delta.com> wrote in article
><01bbb70f$a1e5b940$0a2920cc@landspeeder.delta.com>...
>[a well-argued but misguided attempt to "prove" that explicit conversion
>operators are necessary]
>
>I did a lot of thinking about a year ago about how to implement a
>basic-type lookalike class. My final decision on the issue was that you can
>do such a thing that translates without ambiguity almost all of the time
>just by being careful in your choice of which conversion operator to
>employ.

Your argument is a reasonable response to Sean's. It is also a good
survival guide for coping with the current state of the world. However,
it does not fully address my issues in a previous post. What if there
were NO conversions that could be done without losing some precision. My
example was of a rational number type which cannot be exactly
represented in any of the built-in types. Still, I wanted to be able to
explicitly cast to those types so that template code could read
something like:

    template <class T>
    void foo(T x) { ...  double(x) ... }

Function foo only works if T is convertable to double via a cast. I now
have a few options

1. Implement Rational::operator double()
   Problem: code like "double x = Rational(1, 7);" compiles without
            warning, even though precision is lost.

2. Specialize foo(Rational)
   Problem: Must identify and specialize every template that uses these

            conversions, including those that haven't been written yet.
            Cumbersome and error prone.

3. Write a function "double toDouble(Rational)" and also
   "double toDouble(int)", etc.) Use toDouble() instead of double() in
   my template code.
   Problem: What should conversion function be called: "toDouble()",
            "Double()", "to_double()", "as_double()" ?
            What if somebody else chooses a different convention for
            their classes and/or templates or uses double() without
            realizing the limitations?

Even with all of its problems, I still think number 3 is the best
work-around. However, it *is* a work-around for something that I think
ought to be fixed within the language.

Are explicit operators strictly "needed?" No. But neither was bool. The
absence of the bool type caused problems precisely because everybody was
using their own conventions: "Bool", "bool", "Boolean", "flag",
"logical", etc. The same problem is at work here.

-------------------------------------------------------------
Pablo Halpern                   phalpern@truffle.ultranet.com

I am self-employed. Therefore, my opinions *do* represent
those of my employer.


[ 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                             ]





Author: bradds@concentric.net (Bradd W. Szonye)
Date: 1996/10/13
Raw View
Sean L. Palmer <sean@delta.com> wrote in article
<01bbb70f$a1e5b940$0a2920cc@landspeeder.delta.com>...
[a well-argued but misguided attempt to "prove" that explicit conversion
operators are necessary]

I did a lot of thinking about a year ago about how to implement a
basic-type lookalike class. My final decision on the issue was that you can
do such a thing that translates without ambiguity almost all of the time
just by being careful in your choice of which conversion operator to
employ.

Note that I used 'conversion operator' in the singular. Your design needs
to determine which of all the possible scalar conversions best represents
your new type, and which you're most likely to use. A fixed-decimal type,
for example, probably maps best to either one of the floating-point types,
or possibly one of the large integers. Given that you can convert
implicitly to "long double," you can always use a narrowing cast to, say,
signed short, just as you can cast a long double to a short.

About the only place where this is a major problem is when you also want to
use operator bool as a 'valid' flag in your type. That's a design, flaw,
however; since converting any type of scalar to bool is already
well-defined as a narrowing value conversion, you need to provide an
explicitly-named boolean validation member function.

The hardest decision to make in emulating a scalar is how well to make it
fit into the existing type system, especially if such conversions 'lose'
information. I'd say, if you have a sensible scalar-emulator that can be
represented exactly by one of the existing types, then provide an
(implicit) conversion operator to the most specific type that will hold the
value, and leave it at that! Let the type system itself perform the rest of
the conversions. The main place you'll then find trouble are template
functions which try to do a double-user-conversion from your custom scalar
type to a fundamental type to another custom scalar type. Workaround for
that: either specialize the template or provide a conversion to *that*
type. Adding a conversion from class to class is much less dangerous than
adding one to each of the built-ins.

To sum up: providing conversions to more than one integral type is a
mistake in C++ as fundamental as calling virtual functions in constructors
or misunderstanding the virtual base system. Conversions and overloading in
C++ are wonderful, but *keep it to a minimum*! Doing so keeps your code
smaller, cleaner, and more maintainable. This I have learned from
experience: if C++ lets you do 10 things in 20 ways, limit yourself to
using 2 of the features and using them the same way each time. Which 2 of
the 10 is your personal choice, which is what I like about C++. The fact
that most people will use 8 of the 10 is one of the things I don't like
about maintaining (shock, gasp) C++ code.

There are two reasons the whole explicit conversion issue is confusing:
early reports on explicit constructors implied that conversion ops would
get the treatment too--or at least people inferred that conclusion--thus
confusing people, and explicit conversion ops aren't really necessary even
though they 'seem' to be necessary. Like the "inherited::" issue or keyword
parameters, there's already enough ways to accomplish what you need.
Explicit constructors were a necessity for the standard library, but
explicit conversion ops are not necessary at all. Without prior art, broken
code, needs in the standard library, or a well-written proposal for
explicit conversion ops, there is no good reason to include them in this
version of the library.
--
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~bradds


[ 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                             ]