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/omnetian@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