Topic: [proposal] "Explicit" keyword for conversion operators


Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 23 May 1994 02:35:24 GMT
Raw View
cbarber@bbn.com (Christopher Barber) writes:

>>>>>> "HF" == Harald Fuchs <hf@informatik.uni-karlsruhe.de> writes:
>
>    HF> You don't need a language extension for that:
>
>    HF>   class X {
>    HF>   public:
>    HF>       class Arg {
>    HF>       public:
>    HF>           Arg (int);
>    HF>       };
>    HF>       X (Arg);
>    HF>   };
>
>    HF>   void f (X) {}
>
>    HF>   int main ()
>    HF>   {
>    HF>       X x (1); // fine
>    HF>       f (x); // fine
>    HF>       f (1); // error
>
>Sure, although it could be a real pain defining all of those argument
>classes.

Just use a single template class.

>But what is to stop this in any case?
>
>              X:Arg arg(1) ;
>              f(arg) ; // whoops!

You're hardly going to do that by accident.

>Furthermore, what do I do if I am not willing to pay the computational
>overhead required here for the extra level of indirection?

Make the constructor for the intermediate class inline.  If that
doesn't help, get a better compiler. There is no actual indirection
required.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: mcelroy@ecst.csuchico.edu (James Robert McElroy)
Date: 17 May 1994 15:10:48 GMT
Raw View
In article <KUHLINS.94May17141342@hawk.wifo.uni-mannheim.de>,
Stefan Kuhlins <kuhlins@hawk.wifo.uni-mannheim.de> wrote:
>
>fraction operator+(const fraction&, const fraction&);
>fraction operator+(const fraction&, double);
>fraction operator+(double, const fraction&);
>
>Now it's clear for the compiler which operator to use.
>But one must write a lot of operators...
>
>- Stefan Kuhlins
>
Exactly.  And with class "fraction", you need to overload:

  + - / * < > <= => != ==   and perhaps others...

The use of the "explicit" keyword (or the older method of defining a member
function such as "asDouble" or "toDouble") allows you to write 10 overloaded
operators (using conversion constructors) rather than 30, thus cutting your
code size down by almost 2/3.

I erroneously wrote in my last letter an example that stated:

> C = B + 5;

> The compiler doesn't know whether to convert 5 to a fraction or C to
> a double.

This should have read:

  "The compiler doesn't know whether to convert 5 go a fraction or B to
  a double."  Sorry about that.

--
Jim McElroy
Calif. State Univ., Chico
mcelroy@ecst.csuchico.edu




Author: hf@informatik.uni-karlsruhe.de (Harald Fuchs)
Date: 17 May 1994 16:57:28 GMT
Raw View
In article <CBARBER.94May17115656@apricot.bbn.com>, cbarber@bbn.com (Christopher Barber) writes:

JM> The "explicit" keyword would help prevent the ambiguity problems,
JM> while retaining the natural "feel" of C++.  In addition, it would
JM> be backward compatible with older C++ code (in the sense that old
JM> code without the "explicit" keyword would still work -- the
JM> "explicit" keyword is optional).

> I like this.  Perhaps the same could be done for constructors that take
> a single argument, I have been bitten more than once by automatic
> conversions due to such constructors.

You don't need a language extension for that:

  class X {
  public:
    class Arg {
    public:
      Arg (int);
    };
    X (Arg);
  };

  void f (X) {}

  int main () {
    X x (1);      // fine
    f (x);        // fine
    f (1);        // error
    return 0;
  }
--
Harald Fuchs <hf@informatik.uni-karlsruhe.de>




Author: cbarber@bbn.com (Christopher Barber)
Date: 17 May 1994 23:34:12 GMT
Raw View
>>>>> "HF" == Harald Fuchs <hf@informatik.uni-karlsruhe.de> writes:

    HF> In article <CBARBER.94May17115656@apricot.bbn.com>, cbarber@bbn.com
    HF> (Christopher Barber) writes:

    JM> The "explicit" keyword would help prevent the ambiguity problems,
    JM> while retaining the natural "feel" of C++.  In addition, it would
    JM> be backward compatible with older C++ code (in the sense that old
    JM> code without the "explicit" keyword would still work -- the
    JM> "explicit" keyword is optional).

    >> I like this.  Perhaps the same could be done for constructors that
    >> take a single argument, I have been bitten more than once by
    >> automatic conversions due to such constructors.

    HF> You don't need a language extension for that:

    HF>   class X {
    HF>   public:
    HF>       class Arg {
    HF>       public:
    HF>           Arg (int);
    HF>       };
    HF>       X (Arg);
    HF>   };

    HF>   void f (X) {}

    HF>   int main ()
    HF>   {
    HF>       X x (1); // fine
    HF>       f (x); // fine
    HF>       f (1); // error

Sure, although it could be a real pain defining all of those argument
classes.  But what is to stop this in any case?

              X:Arg arg(1) ;
              f(arg) ; // whoops!

    HF>       return 0;
    HF>   }

Furthermore, what do I do if I am not willing to pay the computational
overhead required here for the extra level of indirection?

- Chris
--
Christopher Barber
(cbarber@bbn.com)




Author: kuhlins@hawk.wifo.uni-mannheim.de (Stefan Kuhlins)
Date: 18 May 1994 13:56:03 GMT
Raw View
In article <HF.94May17185728@faustino.informatik.uni-karlsruhe.de> hf@informatik.uni-karlsruhe.de (Harald Fuchs) writes:

>  In article <CBARBER.94May17115656@apricot.bbn.com>, cbarber@bbn.com (Christopher Barber) writes:

>  JM> The "explicit" keyword would help prevent the ambiguity problems,
>  JM> while retaining the natural "feel" of C++.  In addition, it would
>  JM> be backward compatible with older C++ code (in the sense that old
>  JM> code without the "explicit" keyword would still work -- the
>  JM> "explicit" keyword is optional).

>  > I like this.  Perhaps the same could be done for constructors that take
>  > a single argument, I have been bitten more than once by automatic
>  > conversions due to such constructors.

>  You don't need a language extension for that:

>    class X {
>    public:
>      class Arg {
>      public:
  Arg (int);
>      };
>      X (Arg);
>    };

>    void f (X) {}

>    int main () {
>      X x (1);      // fine     line 15
>      f (x);        // fine
>      f (1);        // error    line 17
>      return 0;
>    }

X x (1); should be the same as X x=1;
But my compilers don't like it:

CC: SC3.0 15 Dec 1993
  line 15: Error: Cannot use int to initialize X.

g++ 2.5.7
  15: no constructor named `Arg' in visible scope
  15: in conversion to type `X::Arg'
  17: no constructor named `Arg' in visible scope
  17: in conversion to type `X::Arg'


The ARM says on page 271:

    class X { /* ... */ X(int); };
    class Y { /* ... */ Y(X); };
    Y a = 1;                    // illegal: Y(X(1)) not tried

- Stefan Kuhlins





Author: jason@cygnus.com (Jason Merrill)
Date: Wed, 18 May 1994 20:34:05 GMT
Raw View
>>>>> Stefan Kuhlins <kuhlins@hawk.wifo.uni-mannheim.de> writes:

> X x (1); should be the same as X x=1;
> But my compilers don't like it:

> g++ 2.5.7
>   15: no constructor named `Arg' in visible scope
>   15: in conversion to type `X::Arg'
>   17: no constructor named `Arg' in visible scope
>   17: in conversion to type `X::Arg'

This is a g++ bug which will be fixed in 2.6.0.

Jason




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Thu, 19 May 1994 09:03:42 GMT
Raw View
In article <2r86d8$22l@charnel.ecst.CSUChico.EDU> mcelroy@ecst.csuchico.edu (James Robert McElroy) writes:
>The "explicit" keyword would help prevent the ambiguity problems, while
>retaining the natural "feel" of C++.   In addition, it would be backward
>compatible with older C++ code (in the sense that old code without the
>"explicit" keyword would still work -- the "explicit" keyword is optional).
>What do you think?

The problem in C++ is *not* that we have no way of *preventing* user-defined
type conversion operators from being called implicitly (on some selected
occasions).  It is that they *can* be called implicitly ever.

Merely preventing *some* of the implicit calls is not, IMHO, the answer.

The right answer is simply to remove this whole (implicit user-defined type
conversion call) ``feature'' from the language entirely.

It causes nothing but problems, both in the language definition and for
practicing programmers.  It leads to more ambiguity (both in the langauge
definition and in practice) than you can shake a stick at.

God help the maintenance programmers who have to try to read code containing
these invisible secret calls all over the place.

-- rfg
-- Alumni of CSU, Chico

--

-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- domain addr: rfg@netcom.com ----------- Purveyors of Compiler Test ----
---- uucp addr: ...!uunet!netcom!rfg ------- Suites and Bullet-Proof Shoes -




Author: kanze@us-es.sel.de (James Kanze)
Date: 19 May 1994 15:04:24 GMT
Raw View
In article <CBARBER.94May17115656@apricot.bbn.com> cbarber@bbn.com
(Christopher Barber) writes:

|> I like this.  Perhaps the same could be done for constructors that take
|> a single argument, I have been bitten more than once by automatic
|> conversions due to such constructors.

So don't write constructors that take a single argument if you don't
want conversion.

(Half?) seriously:

 enum NoConvert { constructFrom } ;

 class MyClass
 {
 public :
   MyClass( NoConvert , int ) ;
     // ...
 } ;

Use:

 MyClass  a( constructFrom , 10 ) ;

Obviously, the constructor ignores the first parameter completely.

And despite the "(Half?)" above, if I were writing a vector class with
a constructor which requires an int for the dimension, I would
definitly consider something like the above.
--
James Kanze                       email: kanze@lts.sel.alcatel.de
GABI Software, Sarl., 8 rue du Faisan, F-67000 Strasbourg, France
Conseils en informatique industrielle --
                   -- Beratung in industrieller Datenverarbeitung




Author: bwh@beach.cis.ufl.edu (Brian Hook)
Date: 19 May 1994 19:32:16 GMT
Raw View
Unfortunately, implicit conversion could be considered necessary because of
the sheer number of explicit operations involving different types that lack
of a type conversion facility would allow.  Refer to "Design and Evolution
of C++" by Stroustrup, page 80, for a reasoning for the inclusion of
implicit type conversions.

In a nutshell, if you were forced to do an explicit cast all the time or,
worse yet, forced to write a different type of function for each of the
many mutable types, class definitions would become rather large and
obfuscated.  Witness the 'complex' class:  defining the full set of
mixed-mode operations requires 12 arithmetic functions compared to 3 plus a
conversion function when implicit conversion is used.

Stroustrup states:

"Where the number of operations and the number of types involved are
higher, the difference between the linear increase in the number of
functions that we get from using conversion and quadratic explosion we get
from requiring all combinations becomes significant."

Brian

PS I do believe that implicit type conversion is WAY overused and
needlessly obfuscates aspects of the language since it's so easy to abuse
and too many programmers think that it's "cool".  Take a fixed point class
-- wouldn't a "Double()" and "Float()" and "Int()" set of member functions
be a lot clearer than the implicit conversions?  I think so, mainly because
it is too easy for the wrong cast to be called.



--
+-----------------------------------------------------------------+
| Brian Hook            | Specializing in real-time 3D graphics   |
| Box 90315             |-----------------------------------------|
| Gainesville, FL 32607 | Internet: bwh@cis.ufl.edu | Free Tibet! |
+-----------------------------------------------------------------+




Author: mcelroy@ecst.csuchico.edu (James Robert McElroy)
Date: 22 May 1994 03:40:23 GMT
Raw View
In article <rfgCq1L67.A5o@netcom.com :,
Ronald F. Guilmette <rfg@netcom.com : wrote:
 :In article <2r86d8$22l@charnel.ecst.CSUChico.EDU : mcelroy@ecst.csuchico.edu (James Robert McElroy) writes:
 : :The "explicit" keyword would help prevent the ambiguity problems, while
 : :retaining the natural "feel" of C++.   In addition, it would be backward
 : :compatible with older C++ code (in the sense that old code without the
 : :"explicit" keyword would still work -- the "explicit" keyword is optional).
 : :What do you think?
 :
 :The problem in C++ is *not* that we have no way of *preventing* user-defined
 :type conversion operators from being called implicitly (on some selected
 :occasions).  It is that they *can* be called implicitly ever.
 :
 :Merely preventing *some* of the implicit calls is not, IMHO, the answer.
 :
 :The right answer is simply to remove this whole (implicit user-defined type
 :conversion call) ``feature'' from the language entirely.
 :
 :It causes nothing but problems, both in the language definition and for
 :practicing programmers.  It leads to more ambiguity (both in the langauge
 :definition and in practice) than you can shake a stick at.
 :
 :God help the maintenance programmers who have to try to read code containing
 :these invisible secret calls all over the place.
 :
 :-- rfg
 :-- Alumni of CSU, Chico

I agree completely.  The only problem is all that old code that would "break"
with the new compiler.  (Paul Luker agrees with you completely, too.)

This is my first few weeks exploring the network seriously.  You people sure
are nice compared to some of the other newsgroups around.  If you really
want to see some "bullets flying", try alt.conspiracy.jfk.  Good God, if
those people ever met in person, they'd kill each other.

--
Jim McElroy
Calif. State Univ., Chico
mcelroy@ecst.csuchico.edu




Author: mcelroy@ecst.csuchico.edu (James Robert McElroy)
Date: 16 May 1994 16:18:48 GMT
Raw View
I am new to this newsgroup, so if I violate any protocol, please be
easy on me -- it is unintentional.

In dealing with classes that require many overloaded operators (e.g. a
"fraction" class that holds an integer numerator and an integer denominator)
it is often the case where everything goes smoothly with overloading until
a conversion operator is added (e.g. convert "fraction" to float or double).
Then, all sorts of conversion ambiguities result, halting compilation.

It seems to me that if an "explicit" keyword could be added to conversion
operators, these problems could be avoided.  For example, in the fraction
class, the keyword could be used as follows:

fraction::operator explicit double(void)
{
    return ((double) numerator)/denominator;
}

What the "explicit" keyword would do is insist that the user of "fraction"
declare a conversion explicitly, as in:

int main(void)
{
   fraction aFraction(2, 3);
   double aDouble;

   aDouble = aFraction;           // WRONG!  WON'T EVEN COMPILE
   aDouble = double(aFraction);   // Will compile and run correctly


}

The "explicit" keyword would help prevent the ambiguity problems, while
retaining the natural "feel" of C++.   In addition, it would be backward
compatible with older C++ code (in the sense that old code without the
"explicit" keyword would still work -- the "explicit" keyword is optional).
What do you think?
--
Jim McElroy
Calif. State Univ., Chico
mcelroy@ecst.csuchico.edu




Author: jason@cygnus.com (Jason Merrill)
Date: Mon, 16 May 1994 22:00:52 GMT
Raw View
>>>>> James Robert McElroy <mcelroy@ecst.csuchico.edu> writes:

> fraction::operator explicit double(void)
> {
>     return ((double) numerator)/denominator;
> }

What does this offer that

double fraction::as_double(void)

does not?

Jason




Author: dag@control.lth.se (Dag Bruck)
Date: 16 May 1994 19:44:01 GMT
Raw View
>>>>> "J" == James Robert McElroy <mcelroy@ecst.csuchico.edu> writes:

J> I am new to this newsgroup, so if I violate any protocol, please be
J> easy on me -- it is unintentional.

J> In dealing with classes that require many overloaded operators
J> (e.g. a "fraction" class that holds an integer numerator and an
J> integer denominator) it is often the case where everything goes
J> smoothly with overloading until a conversion operator is added
J> (e.g. convert "fraction" to float or double).  Then, all sorts of
J> conversion ambiguities result, halting compilation.

J> fraction::operator explicit double(void)
J> {
J> return ((double)numerator)/denominator;
J> }

I agree that the problem exists.  I usually write such explicit
conversions as a plain function:

 double fraction::toDouble() const { .... }

 fraction f;
 double d = f.toDouble();

-- Dag




Author: mcelroy@ecst.csuchico.edu (James Robert McElroy)
Date: 17 May 1994 03:11:42 GMT
Raw View
It (explicit type conversion) offers two things:  first, you can declare

A = double(foo);
or
A = (double)foo;

Instead of having to guess what the program writer used for conversions:

A = foo.asDouble();
A = foo.toDouble();
A = foo.To_Double();
A = foo.convertToDouble();

etc.....

Second, as demonstrated in the above code, the conversion operator does not
require that you specify which class the operator belongs to -- the compiler
figures this out.  So again, the following works:

A = double(foo);
or
A = (double)foo;

rather than the dot notation:

A = foo.asDouble();

If you are not familiar with conversion operators as they exist today in C++,
they allow you to implement the following:

int main(void)
{
   fraction foo(2,3);  // for example
   double A;

   A = foo;   // compiler takes care of figuring out what is going on
   // OR
   A = (double) foo;      // either case works right now
}

The problem is, in case # 1, the compiler will INSIST on making the conversion.
This leads to the following problems:

Let's say you have declared a conversion constructor that takes just one
integer for "fraction".  E.g.

fraction(int numerator, int denominator = 1);  // declaration

and you have also declared/defined an overloaded operator:

fraction::operator + (right fraction)
{
    return blah blah blah;  // can't think right now
}

and you have also defined a conversion oerator:

fraction::operator double()
{
   return ((double) numerator)/denominator;
}

Now, when you write the following in main:

int main(void)
{
   fraction A(1, 2), B(3, 4), C;
   double X;

   C = A + 5;  // PROBLEMS!!!!!

}

The darned compiler doesn't know whether to convert 5 up to a fraction, or
to convert C down to a double.  However, with explicit casts, this is
avoided.  You could write:

int main(void)
{
 fraction A(1, 2), B(3, 4), C;
 double X;

 C = A + 5;    // with explicit, MUST convert 5 to a fraction
 X = (double)A + 5;   // must convert A to a double

 }

 It may look like a minor thing, but if you've tangled with this sort of
 problem, it doesn't seem minor.

--
Jim McElroy
Calif. State Univ., Chico
mcelroy@ecst.csuchico.edu




Author: kuhlins@hawk.wifo.uni-mannheim.de (Stefan Kuhlins)
Date: 17 May 1994 12:13:42 GMT
Raw View
In article <2r86d8$22l@charnel.ecst.CSUChico.EDU> mcelroy@ecst.csuchico.edu (James Robert McElroy) writes:

>   In dealing with classes that require many overloaded operators (e.g. a
>   "fraction" class that holds an integer numerator and an integer denominator)
>   it is often the case where everything goes smoothly with overloading until
>   a conversion operator is added (e.g. convert "fraction" to float or double).
>   Then, all sorts of conversion ambiguities result, halting compilation.

You can avoid this ambiguities, if you write all operators three times:

fraction operator+(const fraction&, const fraction&);
fraction operator+(const fraction&, double);
fraction operator+(double, const fraction&);

Now it's clear for the compiler which operator to use.
But one must write a lot of operators...

- Stefan Kuhlins





Author: cbarber@bbn.com (Christopher Barber)
Date: 17 May 1994 15:56:56 GMT
Raw View
>>>>> "JM" == James Robert McElroy <mcelroy@ecst.csuchico.edu> writes:

    JM> In dealing with classes that require many overloaded operators
    JM> (e.g. a "fraction" class that holds an integer numerator and an
    JM> integer denominator) it is often the case where everything goes
    JM> smoothly with overloading until a conversion operator is added
    JM> (e.g. convert "fraction" to float or double).  Then, all sorts of
    JM> conversion ambiguities result, halting compilation.

    JM> It seems to me that if an "explicit" keyword could be added to
    JM> conversion operators, these problems could be avoided.  For
    JM> example, in the fraction class, the keyword could be used as
    JM> follows:

    JM> fraction::operator explicit double(void)
    JM> {
    JM>     return ((double)numerator)/denominator;
    JM> }

    JM> What the "explicit" keyword would do is insist that the user of
    JM> "fraction" declare a conversion explicitly, as in:

    JM> int main(void)
    JM> {
    JM>     fraction aFraction(2, 3);
    JM>     double aDouble;
    JM>
    JM>     aDouble = aFraction; // WRONG!  WON'T EVEN COMPILE
    JM>     aDouble = double(aFraction); // Will compile and run correctly
    JM> }

    JM> The "explicit" keyword would help prevent the ambiguity problems,
    JM> while retaining the natural "feel" of C++.  In addition, it would
    JM> be backward compatible with older C++ code (in the sense that old
    JM> code without the "explicit" keyword would still work -- the
    JM> "explicit" keyword is optional).

I like this.  Perhaps the same could be done for constructors that take
a single argument, I have been bitten more than once by automatic
conversions due to such constructors.

- Chris

--
Christopher Barber
(cbarber@bbn.com)