[comp.lang.c++] Inheritance & type conversion

skip@taygeta.oc.nps.navy.mil (Skip Carter) (06/06/91)

	I have been programming C++ for the past few months, using Turbo C++
        V1.0 and Zortech C++ V2.1.  In the process of writing an application
        I came across the following behaviour, which I don't understand:


        There are two classes, Base and Derived.
        
        Derived is derived from the Base class and has a method to do a
        	type conversion from Derived TO Base.

	Base has friends functions, report1 and report2.  Report1 takes a
        	reference to a Base, report2 takes the value of a Base,
                as the parameter.

	The following:                
                
        	Derived dtype;

        	report1( dtype );
               
      	causes a conversion to Base then report1 is invoked with Turbo C++,
        but for Zortech C++ NO CONVERSION occurs.
        
        
        BUT:
                  
        	Derived dtype;

        	report2( dtype );
               

	cases a conversion to Base with Zortech C++, BUT NOT for Turbo C++     
          



	I am too much of a novice to be able to figure out which is the
        correct behavior.  Can anyone give me some insight ?  I have attached
        a copy of the simple program.
        

	Here is the program output for Turbo C++ V1.0:
        
		Type conversion from Derived to Base invoked
		report1 reporting
		report2 reporting


	Here is the program output for Zortech C++ V2.1:

		report1 reporting
		Type conversion from Derived to Base invoked
		report2 reporting
                
                
        (Note I have abstracted the classes from the relatively complicated
         classes in the actual application in order to show the behaviour).
         

 
	The code:


        
// inhtest.cpp		tests the interaction of type conversion
//			and inheritance


// Note:
//	 report1( Derived )	causes a conversion to Base
//				then a call report1( Base )
//                              with TURBO C++ V1.0, with Zortech C++ V2.1
//				no conversion is made

//	 report2( Derived )	causes the OPPOSITE to happen
//


#ifdef __ZTC__
#include <stream.hpp>
#else
#include <iostream.h>
#endif

class Base {

	public:
           Base() {};
           ~Base() {};
        friend void report1(Base& x);
        friend void report2(Base  x);
};

void report1(Base& x)
{
	 cout << "report1 reporting\n";
}

void report2(Base x)
{
	 cout << "report2 reporting\n";
}



class Derived : public Base {

	public:
        	Derived() {};
                ~Derived() {};
                operator Base();	// cast to Base
};


// a type cast from a Derived directly to a Base
Derived::operator Base()		// Base m = Derived a
{
	Base dummy;
        
	cout << "Type conversion from Derived to Base invoked\n";
        
        return dummy;
}


void main()
{
        Derived dtype;

        report1( dtype );
               
        report2( dtype );        
}



   Everett (Skip) Carter		Phone:  408-646-3318
   Naval Postgraduate School	        INTERNET: skip@taygeta.oc.nps.navy.mil
   Dept. of Oceanography, Code OC/CR    UUCP:     ...!uunet!taygeta!skip
   Monterey, CA. 93934                  TELEMAIL: s.carter/omnet

ian@rathe.cs.umn.edu (Ian Hogg) (06/06/91)

In article <2348@taurus.cs.nps.navy.mil> skip@taygeta.oc.nps.navy.mil (Skip Carter) writes:
>
>	I have been programming C++ for the past few months, using Turbo C++
>        V1.0 and Zortech C++ V2.1.  In the process of writing an application
>        I came across the following behaviour, which I don't understand:
>
>
>        There are two classes, Base and Derived.
>        
>        Derived is derived from the Base class and has a method to do a
>        	type conversion from Derived TO Base.

		You don't need to define a conversion from Derived to Base.  An object
		of type derived can be used anywhere an object of base is expected.
>
>	Base has friends functions, report1 and report2.  Report1 takes a
>        	reference to a Base, report2 takes the value of a Base,
>                as the parameter.
>
>	The following:                
>                
>        	Derived dtype;
>
>        	report1( dtype );
>               
>      	causes a conversion to Base then report1 is invoked with Turbo C++,
>        but for Zortech C++ NO CONVERSION occurs.
>        
>        
>        BUT:
>                  
>        	Derived dtype;
>
>        	report2( dtype );
>               
>
>	cases a conversion to Base with Zortech C++, BUT NOT for Turbo C++     
>          
>
>
>
>	I am too much of a novice to be able to figure out which is the
>        correct behavior.  Can anyone give me some insight ?  I have attached
>        a copy of the simple program.
>        

   I don't know which is correct but I would expect no conversion to take place
   in either place.  The compiler should only apply conversions whenever 
   necessary, and they aren't in this case.


-- 
Ian Hogg                        email:  rathe!ian@cs.umn.edu
                                        ...!umn-cs!rathe!ian
Rathe, Inc                              ianhogg@cs.umn.edu
366 Jackson Street              phone:  (612) 225-1401

steve@taumet.com (Stephen Clamage) (06/07/91)

skip@taygeta.oc.nps.navy.mil (Skip Carter) writes:

>There are two classes, Base and Derived.

>Derived is derived from the Base class and has a method to do a
>	type conversion from Derived TO Base.

>Base has friends functions, report1 and report2.  Report1 takes a
>	reference to a Base, report2 takes the value of a Base,
>	as the parameter.

[
  sometimes the user conversion from Derived to Base is called for
  only one function, sometimes only for the other, depending on compiler.
]

The problem is that the language specification defines how a derived
class is converted to a base class and under certain circumstances
must result in a slice of the same object.  That is, under some
circumstances the conversion must NOT call a user conversion function.

Some cases are (at the moment) undefined as to whether a user conversion
will be called.  This leaves room for different results from different
implementations.

Therefore, it is bad practice to declare a user conversion from a
derived to a base class, since it sometimes will not be called.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

sakkinen@jyu.fi (Markku Sakkinen) (06/13/91)

I have not followed this group regularly in recent times.
Pardon me if there has already been a correct answer to the original
question, but I have only seen the following incorrect one.

In article <1991Jun6.140159.9208@rathe.cs.umn.edu> ian@rathe.cs.umn.edu (Ian Hogg) writes:
>In article <2348@taurus.cs.nps.navy.mil> skip@taygeta.oc.nps.navy.mil (Skip Carter) writes:
>> ...
>>        There are two classes, Base and Derived.
>>        
>>        Derived is derived from the Base class and has a method to do a
>>        	type conversion from Derived TO Base.
>
>		You don't need to define a conversion from Derived to Base.  An object
>		of type derived can be used anywhere an object of base is expected.

Not quite true.  When assigning a _value of_ (not a reference to) Derived
to a Base variable, the default operation simply copies the data members
of Base and ignores those added in Derived.  If that is not desired,
a conversion function has to be written.  -- However, it might happen
that people unnecessarily write conversion functions to achieve just
what they would get with the default conversion.

>>	Base has friends functions, report1 and report2.  Report1 takes a
>>        	reference to a Base, report2 takes the value of a Base,
>>                as the parameter.
>>
>>	The following:                
>>                
>>        	Derived dtype;
>>
>>        	report1( dtype );
>>               
>>      	causes a conversion to Base then report1 is invoked with Turbo C++,
>>        but for Zortech C++ NO CONVERSION occurs.

Turbo seems to be wrong, Zortech right.

>>        BUT:
>>                  
>>        	Derived dtype;
>>
>>        	report2( dtype );
>>               
>>
>>	cases a conversion to Base with Zortech C++, BUT NOT for Turbo C++     

Again Turbo does it wrong and Zortech right!  Score 2 - 0 for Zortech.
An aggravating circumstance is that the pertinent rules in the language
definition have been the same already before Release 2.0.  It is thus
not a question about a novelty, which implementors could be uncertain about
or might not have been able to take into account yet.
Some years ago, Turbo Pascal seemed to be more Turbo than Pascal
in several aspects.   Is that the current case with Turbo C++?

>>        ...
>
>   I don't know which is correct but I would expect no conversion to take place
>   in either place.  The compiler should only apply conversions whenever 
>   necessary, and they aren't in this case.

Your doubt about your answer was well founded :-)

----------------------------------------------------------------------
"All similarities with real persons and events are purely accidental."
      official disclaimer of news agency New China

Markku Sakkinen (sakkinen@jytko.jyu.fi)
       SAKKINEN@FINJYU.bitnet (alternative network address)
Department of Computer Science and Information Systems
University of Jyvaskyla (a's with umlauts)
PL 35
SF-40351 Jyvaskyla (umlauts again)
Finland
----------------------------------------------------------------------

sakkinen@jyu.fi (Markku Sakkinen) (06/14/91)

(Arrrgh: I already wrote and submitted something like this yesterday,
but some testing of the news system here caused all articles to get lost.
Worse, the previous - in my opinion incorrect - answer to the original
question that I had commented on, has expired in the meantime.)

In article <761@taumet.com> steve@taumet.com (Stephen Clamage) writes:
>skip@taygeta.oc.nps.navy.mil (Skip Carter) writes:
>
>>There are two classes, Base and Derived.
>
>>Derived is derived from the Base class and has a method to do a
>>	type conversion from Derived TO Base.

The comment that I lost said that there is never any need for such
a conversion.  That is not quite true:  if the semantics of the default
assignment operator or copy constructor called ("slicing" below)
is not what is desired, one has to write a conversion function.

>>Base has friends functions, report1 and report2.  Report1 takes a
>>	reference to a Base, report2 takes the value of a Base,
>>	as the parameter.
>
>[
>  sometimes the user conversion from Derived to Base is called for
>  only one function, sometimes only for the other, depending on compiler.
>]

Skip Carter said that when the functions were invoked on an actual
parameter of declared type Derived:
- with 'report1', Turbo C++ invoked the conversion, Zortech C++ did not.
- with 'report2', Turbo C++ did not invoke the conversion, Zortech C++ did.
In both cases, I claim that Zortech is right and Turbo C++ wrong.

Some years ago, Turbo Pascal seemed to be a lot more Turbo than Pascal
on several aspects.  Maybe that is the current situation with Turbo C++?

>The problem is that the language specification defines how a derived
>class is converted to a base class and under certain circumstances
>must result in a slice of the same object.  That is, under some
>circumstances the conversion must NOT call a user conversion function.

I don't believe this.  The language specification defines only the
default conversion that is applied IF no user-defined function exists.

>Some cases are (at the moment) undefined as to whether a user conversion
>will be called.  This leaves room for different results from different
>implementations.

I don't believe this either.  Can you cite the pertinent sections
of the ARM?

>Therefore, it is bad practice to declare a user conversion from a
>derived to a base class, since it sometimes will not be called.

Well...  There _is_ one problem caused by the fact that C++ supports
only single polymorphism (late binding takes into account only the
dynamic type of the 'self' object, although compile-time polymorphism
considers the types of all parameters).  The problem was illustrated
in part by 'report2' above.

Note that whenever any conversion function is applied, it yields
a _new_ object as its result - there is no way in C++ to change
the type of an object so that it remains the same object.
Therefore, if a conversion were applied to a reference parameter,
the effect would actually be that of a value parameter.
_That_ would be truly treacherous.

----------------------------------------------------------------------
"All similarities with real persons and events are purely accidental."
      official disclaimer of news agency New China

Markku Sakkinen (sakkinen@jytko.jyu.fi)
       SAKKINEN@FINJYU.bitnet (alternative network address)
Department of Computer Science and Information Systems
University of Jyvaskyla (a's with umlauts)
PL 35
SF-40351 Jyvaskyla (umlauts again)
Finland
----------------------------------------------------------------------

pkt@lpi.liant.com (Scott Turner) (06/19/91)

skip@taygeta.oc.nps.navy.mil (Skip Carter) writes:
>         There are two classes, Base and Derived.
>         
>         Derived is derived from the Base class and has a method to do a
>         	type conversion from Derived TO Base.
> 
> 	Base has friends functions, report1 and report2.  Report1 takes a
>         	reference to a Base, report2 takes the value of a Base,
>                 as the parameter.
...         
>         	Derived dtype;
> 
>         	report2( dtype );
>                
> 	cases a conversion to Base with Zortech C++, BUT NOT for Turbo C++

sakkinen@jyu.fi (Markku Sakkinen) writes:
> [Opinion that Zortech handles both cases right and Turbo wrong.]
> Some years ago, Turbo Pascal seemed to be a lot more Turbo than Pascal
> on several aspects.  Maybe that is the current situation with Turbo C++?

Not at all.  The C++ part of Turbo C++ was developed outside of
Borland by people who did all they could to conform to de facto and
documented standards.

> >Some cases are (at the moment) undefined as to whether a user conversion
> >will be called.  This leaves room for different results from different
> >implementations.
> 
> I don't believe this either.  Can you cite the pertinent sections
> of the ARM?

	p. 288 of the ARM says, "The initialization that occurs in 
	argument passing and function return is equivalent to the form
		T x = a;"

	p. 284 says, "Alternatively a single value is specified as the
	initializer using the = operator.  This value is used as the 
	argument to a copy constructor."

	Section 12.3.2 of the ARM describes how a conversion function
	defines a conversion, but does not specify how the conversion
	function might override or be used in preference to any other
	conversion.  It does say, "User-defined conversions are used
	implicitly only if they are unambiguous."

	Chapter 4 describes the built-in conversion from 
	reference-to-derived to reference-to-base, but does not 
	mandate conversion from derived to base.

That's all the ARM says, outside of commentary and examples, none of
which directly address our subject.  Since the copy constructor takes
an argument of type "const T&", p. 284 implies that

	T x = a;

is equivalent to

	const T & ref = a;
	T x (ref);

This reasoning supports an automatic conversion from derived to base
in most cases, and would explain its omission from chapter 4 of the
ARM.  Note also that it supports Turbo's interpretation of Carter's
"report2" example, not Zortech's and cfront's.

Disclaimer:  The developers of the C++ part of Turbo have consulted for LPI.
--
Prescott K. Turner, Jr.
Language Processors, Inc.
959 Concord St., Framingham, MA 01701 USA    (508) 626-0006
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com