Topic: Constructors and conversion operator


Author: Roman Lechtchinsky <wolfro@cs.tu-berlin.de>
Date: 1996/03/19
Raw View
Steve Clamage wrote:
>
> In article 3438@cs.tu-berlin.de, Roman Lechtchinsky
> <wolfro@cs.tu-berlin.de> writes:
> >
> >I have a question about conversion operators and constructors. Consider the
> >following:
> >
> >class X
> >{
> >       X( Y );
> >};
> >
> >class Y
> >{
> >       operator X();
> >};
>
> A constructor taking a single argument is a type conversion operator from
> the argument type to the class type. The arrangement you show is always
> going to lead to ambiguity errors. There are two ways to convert a Y
> to an X, and neither is preferred over the other. Operator conversion
> functions should normally be avoided, partly for this reason, and partly
> because they can wind up being used in surprising circumstances. The
> compiler might find a conversion that allows erroneous code to compile.
>
> >Now the second one:
> >
> >class A
> >{
> >               A(B);
> >};
> >
> >class B
> >{
> >               operator A&();
> >};
>
> This is the same situation. There is no syntactic difference between
> using a reference or an object. Once again, there are two ways to
> make an A out of a B, and neither is preferred.
>

First of all, sorry: there is a mistake in the example, the constructor
should be A(B&). However, it doesn't matter since both cases are only a
variation of the following problem:

The DWP defines:

A constructor declared without the  function-specifier  explicit  that
  can  be called with a single parameter specifies a conversion from the
  type of its first parameter to the type of its  class. [class.conv.ctor]

Now, to me this means that a copy constructor is a converting constructor
since it takes a single parameter which is a reference to an object of the
class ( I don't think that this is intended ). If this is true a copy
constructor is a user-defined conversion ( after all, A and A& are different
types, aren't they ). This implies that the following call to foo:

foo(b)

is ill-formed ( not ambiguous ) since it requires either b=>B(b)=>A(B(b)) or
b=>A&(b)=>A(A&(b)) but only one user-defined conversion may be applied ( note
that foo is defined as foo(A), not foo(A&) ).


Bye

Roman
---
[ 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: clamage@Eng.sun.com (Steve Clamage)
Date: 1996/03/19
Raw View
In article 76E5@cs.tu-berlin.de, Roman Lechtchinsky <wolfro@cs.tu-berlin.de> writes:
>
>The DWP defines:
>
>A constructor declared without the  function-specifier  explicit  that
>  can  be called with a single parameter specifies a conversion from the
>  type of its first parameter to the type of its  class. [class.conv.ctor]
>
>Now, to me this means that a copy constructor is a converting constructor
>since it takes a single parameter which is a reference to an object of the
>class ( I don't think that this is intended ).

I don't think it matters. The conversion is the identity conversion. In
places where conversions are required, there is almost always more than
one way to achieve the conversion. (Often there is an infinite number
of possible conversion sequences, such as short->int->short->int->short->...->int.)
The explicit rule in such cases is to choose the shortest possible sequence
of conversions.

Suppose a copy constructor T::T(const T&) is viewed as a conversion from T to
T. If such a conversion is "needed", the shortest conversion sequence must be
chosen. That would be the null sequence, and so the copy ctor would never
be used or even considered for that purpose. (Just as the sequence
int->Rational->float would never be considered in converting an int to
a float.)

---
Steve Clamage, stephen.clamage@eng.sun.com



[ 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: Roman Lechtchinsky <wolfro@cs.tu-berlin.de>
Date: 1996/03/21
Raw View
Steve Clamage wrote:
>
> In article 76E5@cs.tu-berlin.de, Roman Lechtchinsky <wolfro@cs.tu-berlin.de>
> writes:
> >
> >The DWP defines:
> >
> >A constructor declared without the  function-specifier  explicit  that
> >  can  be called with a single parameter specifies a conversion from the
> >  type of its first parameter to the type of its  class. [class.conv.ctor]
> >
> >Now, to me this means that a copy constructor is a converting constructor
> >since it takes a single parameter which is a reference to an object of the
> >class ( I don't think that this is intended ).
>
> I don't think it matters. The conversion is the identity conversion. In
> places where conversions are required, there is almost always more than
> one way to achieve the conversion. (Often there is an infinite number
> of possible conversion sequences, such as short->int->short->int->
> short->...->int.) The explicit rule in such cases is to choose the
> shortest possible sequence of conversions.
>
> Suppose a copy constructor T::T(const T&) is viewed as a conversion
> from T to T. If such a conversion is "needed", the shortest conversion
> sequence must be chosen. That would be the null sequence, and so the
> copy ctor would never be used or even considered for that purpose.
> (Just as the sequence int->Rational->float would never be considered in
> converting an int to a float.)

I'm not too sure here. First, if the copy constructor is the identity
conversion, why is it called at all? I mean, if the identity conversion is
eliminated from the conversion sequence, why isn't the call to the copy
constructor itself eliminated? Second, if the copy constructor is called, why
is it called only once? Consider

class A { A(const A&); };
void foo(A);
A a;

Now, foo(a) is implicitly converted to foo(A(a)). Why not to foo(A(A(a))?
Clearly, if the copy constructor is a conversion which has to be performed on
a, it *is* performed and it is performed only once, since no conversion is
needed thereafter. If the copy constructor is not a conversion, things get
complicated.
Actually, I can't agree that the copy constructor is the identity conversion.
It is a conversion from a reference to the referenced type, i.e. from A& to A
in this case. A& and A are two different types and the DWP doesn't say that a
conversion from A& to A (i.e. the copy constructor) isn't a conversion or is
to be eliminated from the conversion sequence or whatever... It just isn't
stated explicitly, probably because everybody knows it. I think this is the
problem.

Bye

Roman
---
[ 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                             ]
---
[ 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: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1996/03/21
Raw View
In article 2E03@cs.tu-berlin.de, Roman Lechtchinsky <wolfro@cs.tu-berlin.de> writes:
>Steve Clamage wrote:
>>
>> Suppose a copy constructor T::T(const T&) is viewed as a conversion
>> from T to T. If such a conversion is "needed", the shortest conversion
>> sequence must be chosen. That would be the null sequence, and so the
>> copy ctor would never be used or even considered for that purpose.
>> (Just as the sequence int->Rational->float would never be considered in
>> converting an int to a float.)
>
>I'm not too sure here. First, if the copy constructor is the identity
>conversion, why is it called at all? I mean, if the identity conversion is
>eliminated from the conversion sequence, why isn't the call to the copy
>constructor itself eliminated? Second, if the copy constructor is called, why
>is it called only once? Consider
>
>class A { A(const A&); };
>void foo(A);
>A a;
>
>Now, foo(a) is implicitly converted to foo(A(a)). Why not to foo(A(A(a))?

Let's not confuse type conversion with making a copy. In the call foo(a)
no type conversion is required. Function foo requires an A object, and is
invoked with an A object. But foo does require a copy of the actual argument,
no matter what its origin. In this case foo gets a local copy of 'a'.

Example 2:
 class B { public: operator A(); };
 B b;
 foo(b);
We do need a conversion of 'b' to type A, which is available. A temporary A
object is created by "B::operator A()", and a copy of that is passed to foo.

In both examples a copy of an A object is needed, accomplished as always
by the copy constructor.

---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ 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: Roman Lechtchinsky <wolfro@cs.tu-berlin.de>
Date: 1996/03/22
Raw View
Steve Clamage wrote:
>
> In article 2E03@cs.tu-berlin.de, Roman Lechtchinsky
> <wolfro@cs.tu-berlin.de> writes:
> >Steve Clamage wrote:
> >>
> >> Suppose a copy constructor T::T(const T&) is viewed as a conversion
> >> from T to T. If such a conversion is "needed", the shortest conversion
> >> sequence must be chosen. That would be the null sequence, and so the
> >> copy ctor would never be used or even considered for that purpose.
> >> (Just as the sequence int->Rational->float would never be considered in
> >> converting an int to a float.)
> >
> >I'm not too sure here. First, if the copy constructor is the identity
> >conversion, why is it called at all? I mean, if the identity conversion is
> >eliminated from the conversion sequence, why isn't the call to the copy
> >constructor itself eliminated? Second, if the copy constructor is called,
> >why is it called only once? Consider
> >
> >class A { A(const A&); };
> >void foo(A);
> >A a;
> >
> >Now, foo(a) is implicitly converted to foo(A(a)). Why not to foo(A(A(a))?
>
> Let's not confuse type conversion with making a copy.

No, no, no... Not me, the draft does (IMO). Anyway, this question is a bit
off topic, I think. I'll try to explain this once more.

Part I.

This is what the DWP says about converting constructors:

1) A constructor declared without the  function-specifier  explicit  that
  can  be called with a single parameter specifies a conversion from the
  type of its first parameter to the type of its  class.   Such  a
  constructor is called a converting constructor. [class.conv.ctor]

2) A  nonconverting  constructor  constructs objects just like converting
  constructors, but does so only where a constructor call is  explicitly
  indicated by the syntax. [class.conv.ctor]

This is what the DWP says about copy constructors:

  A constructor for class X is a copy constructor if its first parameter
  is of type X& or const X& and either there are no other parameters  or
  else  all other parameters have default arguments. [class.copy]

This means that per definitionem a copy constructor is a converting
constructor and thus a user-defined conversion. Note that nothing is said
about the types that participate in the conversion. In particular, it is
*not* said that the identity conversion is not a user-defined conversion.
Thus, everything the DWP says about user-defined conversions applies to
copy constructors.
This is what the DWP says about user-defined conversions:

  At most one user-defined conversion (constructor or  conversion
  function) is implicitly applied to a single value. [class.conv.fct]

As you can see, the DWP doesn't even talk about converting types; it
mentions only applying user-defined conversions.
Now, let's return to my example:

class A { A(const A&); };
class B { operator A&(); };
void foo( A );
B b;

The call foo(b) requires that

a) the conversion operator B::A&() be applied to b;
b) the converting constructor A::A(const A&) be applied to the value returned
by the conversion operator B::A&().

Which makes two user-defined conversions implicitly applied to the single
value b. Hence the call is illegal.

Part II.

Now, let's see if the copy constructor is the identity conversion. The copy
constructor takes an argument of type A&, not A ( a constructor like A(A) is
illegal ). Look at the definition of converting constructors and substitute
"the type of its parameter" with A& and "the type of its class" with A. The
copy constructor specifies a conversion from A& to A. AFAIK, A& and A are
still different types. To me, this doesn't look like an identity conversion.

Well, the way you see this is certainly the way it should be. However, even
if I'm missing something, this all is rather unclear and the main advantage
of a standard is (should be) clarity. Thus, IMO the standard should contain
an explicit statement on this topic.

Bye

Roman
---
[ 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: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1996/03/19
Raw View
In article 3438@cs.tu-berlin.de, Roman Lechtchinsky
<wolfro@cs.tu-berlin.de> writes:
>
>I have a question about conversion operators and constructors. Consider the
>following:
>
>class X
>{
> X( Y );
>};
>
>class Y
>{
> operator X();
>};

A constructor taking a single argument is a type conversion operator from
the argument type to the class type. The arrangement you show is always
going to lead to ambiguity errors. There are two ways to convert a Y
to an X, and neither is preferred over the other. Operator conversion
functions should normally be avoided, partly for this reason, and partly
because they can wind up being used in surprising circumstances. The
compiler might find a conversion that allows erroneous code to compile.

>Now the second one:
>
>class A
>{
>  A(B);
>};
>
>class B
>{
>  operator A&();
>};

This is the same situation. There is no syntactic difference between
using a reference or an object. Once again, there are two ways to
make an A out of a B, and neither is preferred.

---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ 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                             ]