[comp.lang.c++] Reference problem??

davidm@uunet.UU.NET (David S. Masterson) (04/25/91)

>>>>> On 25 Apr 91 06:35:47 GMT, [I] said:

Me> #ifdef GPLUSPLUS
Me> #include <stream.h>
Me> #else
Me> #include <iostream.h>
Me> #include <iomanip.h>
Me> #endif

Me> class alpha {			// top of the hierarchy
Me> 	int	value1;
Me> public:
Me> 	alpha(int a) : value1(a) {}
Me> 	virtual void display() {
Me> 		cout << "Alpha value1 " << value1 << "\n";
Me> 	}
Me> };

Me> class beta : public alpha {	// middle of the hierarchy
Me> 	int	value2;
Me> public:
Me> 	beta(int a, int b) : alpha(a), value2(b) {}
Me> 	virtual void display() {
Me> 		alpha::display();
Me> 		cout << "Beta value2 " << value2 << "\n";
Me> 	}
Me> };

Me> class gamma : public beta {		// bottom of the hierarchy
Me> 	int	value3;
Me> public:
Me> 	gamma(int a, int b, int c) : beta(a, b), value3(c) {}
Me> 	virtual void display() {
Me> 		beta::display();
Me> 		cout << "Gamma value3 " << value3 << "\n";
Me> 	}
Me> };

Me> void testfunc(alpha*& parm) {
Me> 	cout << "testfunc display\n";
Me> 	parm = new alpha(40);
Me> 	parm->display();
Me> }

Me> int main() {
Me> 	gamma	g(10, 20, 30);
Me> 	cout << "first display\n";
Me> 	g.display();
Me> 	cout << "second display\n";
Me> 	gamma	*gptr = &g;
Me> 	gptr->display();
Me> 	testfunc((alpha*) gptr);
		// ^-- why is the cast necessary?
Me> 	cout << "third display\n";
Me> 	gptr->display();
Me> }
Me> // End of the program

I don't understand why the cast is necessary.  Without the cast, Oregon C++,
Turbo C++, and G++ tells me something like:

    Warning: Lvalue coerced into temporary to initialize reference type

Can anyone explain why?
--
====================================================================
David Masterson					Consilium, Inc.
(415) 691-6311					640 Clyde Ct.
uunet!cimshop!davidm				Mtn. View, CA  94043
====================================================================
"If someone thinks they know what I said, then I didn't say it!"

wmm@world.std.com (William M Miller) (04/29/91)

cimshop!davidm@uunet.UU.NET (David S. Masterson) writes:
> [long example deleted]
> I don't understand why the cast is necessary.  Without the cast, Oregon C++,
> Turbo C++, and G++ tells me something like:
>
>     Warning: Lvalue coerced into temporary to initialize reference type
>
> Can anyone explain why?

The deleted example essentially boils down to something like:

        class Base { };
        class Derived: public Base { };
        void f(Base*&);

        void g() {
           Derived* p = new Derived;
           f((Base*) p);
           }

This question comes up fairly regularly.  To paraphrase E&S section 4.7, you
can convert an X& to a Y& without a cast if and only if Y is an unambiguous
accessible base class of X.  In this example, Base stands in that
relationship to Derived, but Base* and Derived* do not -- pointers are not
classes!

This distinction is of more than academic concern.  Consider the following
slightly-elaborated example (using structs to allow access while keeping the
line count down):

        struct Base1 {
           int i;
           };

        struct Base2 { };

        struct Derived: Base1, Base2 { };

        Base2 b2;

        void f(Base2*& b2p) {
           b2p = &b2;
           }

        int main() {
           Derived* dp;
           g(dp);       // Illegal -- for example purposes only
           dp->i = 5;
           }

If you run this program, you will scribble on the memory immediately
preceding the "b2" object in static storage.

I hope this example illustrates why a Derived* is not compatible with a
Base*&.  The message you are getting is the same one you get if you try to
do something like

        int i;
        double* dr = i;

The value of "i" will be converted into a double and the reference will be
initialized with that temporary, not with "i".  In the original example, the
Derived* was converted into a Base* and the reference argument initialized
with the resulting temporary.  That's why the current (2.1/E&S) definition
only allows such an initialization if the reference is const (i.e., const
X&) and the target consequently cannot be modified.

-- William M. Miller, Glockenspiel, Ltd.
   wmm@world.std.com

rae@alias.com (Reid Ellis) (04/30/91)

text in []'s omitted for brevity's sake.

David S. Masterson <cimshop!davidm@uunet.UU.NET> writes:
|class alpha { [..] };
|class beta : public alpha { [..] };
|class gamma : public beta { [..] };
|
|void testfunc(alpha*& parm) {
 [..]
|	parm = new alpha(40);
 [..]
|}
|
|int main() {
|	gamma	g(10, 20, 30);
 [..]
|	gamma	*gptr = &g;
 [..]
|	testfunc((alpha*) gptr);
|		// ^-- why is the cast necessary?
 [..]
|}
|
|I don't understand why the cast is necessary.

This has to do with the fact that when you cast from a derived class
to a base class, the value of the pointer can change.  This has been
the case since multiple inheritance came into being in C++ [and quite
possibly, since before then].  In the above program, when you cast
"gamma *gptr" to an "alpha*", the value of gptr may be changed to
point to its "alpha part".  This pointer is then passed in to
testfunc().  testfunc(), which takes a reference to said pointer can
now set it to point at anything that may or may not be a "gamma".  Now
in main() when you return from the call to testfunc(), C++ will have no
idea what to do with gptr.  Should it offset it back to where it was,
relative to the [new] value of its "alpha" pointer?

Basically, you're trying to rip out the guts of a gamma. :)  As long
as a gamma* has the same pointer value as its respective alpha*, your
cast will work.  But if, say you were to do something like this:

	class beta : public something, public alpha { .. };

then your cast would no longer be valid..

						Reid
--
Reid Ellis     1 Trefan Street Apt. E, Toronto ON, M5A 3A9
rae@utcs.toronto.edu        ||               rae@alias.com
CDA0610@applelink.apple.com ||      +1 416 362 9181 [work]