Polymorphic behavior without virtual functions

D

Dave

What is the expected output of this program and why???



#include <iostream>

using namespace std;

class base
{
public:
~base() {cout << "base" << endl;}
};

class derived: public base
{
public:
~derived() {cout << "derived" << endl;}
};

int main()
{
const base &r = derived();
(void) r; // Suppress compiler warning about unused var.
}
 
V

Victor Bazarov

Dave said:
What is the expected output of this program and why???



#include <iostream>

using namespace std;

class base
{
public:
~base() {cout << "base" << endl;}
};

class derived: public base
{
public:
~derived() {cout << "derived" << endl;}
};

int main()
{
const base &r = derived();
(void) r; // Suppress compiler warning about unused var.
}

The output should be

derived
base

After 'main' function finishes and 'r' goes out of scope.

Regarding the subject of your post: there is no polymorphic
behaviour here. A temporary of type 'derived' is created and
later destroyed when a constant reference (bound to its base
class subobject) ceases to exist (see 12.2/5).

Victor
 
D

Dave

Victor Bazarov said:
The output should be

derived
base

After 'main' function finishes and 'r' goes out of scope.

Regarding the subject of your post: there is no polymorphic
behaviour here. A temporary of type 'derived' is created and
later destroyed when a constant reference (bound to its base
class subobject) ceases to exist (see 12.2/5).

Victor

This program would have undefined behavior if I had used a pointer rather
than a reference. I'm trying to understand the underlying language
mechanism that makes this case different, but I'm not sure that there is a
really satisfying answer; the answer may be "it just is"...
 
V

Victor Bazarov

Dave said:
This program would have undefined behavior if I had used a pointer rather
than a reference.

It would be a different program.
I'm trying to understand the underlying language
mechanism that makes this case different, but I'm not sure that there is a
really satisfying answer; the answer may be "it just is"...

If a reference (a const one) is bound to a temporary, the lifetime of
the temporary extends to match the lifetime of the reference. It is
mandated by the Standard, and I've given you the subclause/paragraph
number. What else mechanism you're talking about, I am not sure.

Victor
 
D

Dave

Victor Bazarov said:
It would be a different program.


If a reference (a const one) is bound to a temporary, the lifetime of
the temporary extends to match the lifetime of the reference. It is
mandated by the Standard, and I've given you the subclause/paragraph
number. What else mechanism you're talking about, I am not sure.

Victor

But how is the proper destructor execution sequence occurring given that we
have a const reference to *base*? What causes the *derived* destructor to
be executed? Why would it be executed given that nothing polymorphic is
going on?

Also, the output I actually see is:

derived
base
derived
base

This I don't understand. I would have expected exactly the output you
predicted!

Another interesting point: Suppose I change the definition of r to the
following:

const base &r(derived());

In this case, I get no output at all!

Not sure what's going on with all of this...
 
S

Steven Green

Not sure why you are getting the repeats. I don't get them here.
I am using g++. Maybe it is a compiler issue.
 
J

John Carson

Dave said:
But how is the proper destructor execution sequence occurring given
that we have a const reference to *base*? What causes the *derived*
destructor to be executed? Why would it be executed given that
nothing polymorphic is going on?

I don't know the answer, but this is my guess. Suppose that you simply
entered

Derived();

Then the destructor for the derived class will be called, courtesy of the
mechanism for handling temporaries. I suspect that it is this same mechanism
that is being used when you assign the temporary to a const reference to a
base object. The only effect of the const reference is to defer the
destruction of the temporary; the basic mechanism of destruction is the same
as if no assignment had been made.
Also, the output I actually see is:

derived
base
derived
base

This I don't understand. I would have expected exactly the output you
predicted!

I suspect a bug. With VC++2002, I get the same repeated output as you do.
If, however, I add a constructor, then the output is as expected.

class base
{
public:
base() {cout << "base constructor" << endl;}
~base() {cout << "base destructor" << endl;}
};

class derived: public base
{
public:
derived() {cout << "derived constructor" << endl;}
~derived() {cout << "derived destructor" << endl;}
};
 
V

Victor Bazarov

Dave said:
[...]

Another interesting point: Suppose I change the definition of r to the
following:

const base &r(derived());

In this case, I get no output at all!

Not sure what's going on with all of this...


const base& r(derived());

is a declaration of a function 'r' that takes one argument of type
'derived' and returns a reference to const base. Internal parens
do not matter.

Victor
 
J

John Carson

Victor Bazarov said:
Dave said:
[...]

Another interesting point: Suppose I change the definition of r to
the following:

const base &r(derived());

In this case, I get no output at all!

Not sure what's going on with all of this...


const base& r(derived());

is a declaration of a function 'r' that takes one argument of type
'derived' and returns a reference to const base. Internal parens
do not matter.

Victor

VC++ 2002 doesn't interpret it that way (it treats it as an assignment to r)
but I presume that this is just a bug.

Comeau online, by contrast, does treat it as a function declaration, but
interprets the function's parameter as a pointer to a function that takes no
parameters and returns a derived object. Thus the following compiles:

derived foo()
{
derived d;
return d;
}

int main()
{
const base &r(derived());
r(foo);
}


whereas the following fails with the error message:

error: no suitable conversion function from "derived" to
"derived (*)()" exists


int main()
{
const base &r(derived());
derived dobject;
r(dobject);
}

I don't know if this is a bug or if there is some way in which a parameter
of type

derived()

can legitimately be intepreted as a parameter of type

derived(*)().
 
V

Victor Bazarov

John Carson said:
Victor Bazarov said:
Dave said:
[...]

Another interesting point: Suppose I change the definition of r to
the following:

const base &r(derived());

In this case, I get no output at all!

Not sure what's going on with all of this...


const base& r(derived());

is a declaration of a function 'r' that takes one argument of type
'derived' and returns a reference to const base. Internal parens
do not matter.

Victor

VC++ 2002 doesn't interpret it that way (it treats it as an assignment to r)
but I presume that this is just a bug.

Comeau online, by contrast, does treat it as a function declaration, but
interprets the function's parameter as a pointer to a function that takes no
parameters and returns a derived object.

Yes, that's probably more correct, I've confused it with a situation

someothertype a;
const base& r(derived(a));

where the second 'a' is not an argument to a function-style cast, but
rather a [superfluous] name of an argument in a function declaration.
Thus the following compiles:

derived foo()
{
derived d;
return d;
}

int main()
{
const base &r(derived());
r(foo);
}


whereas the following fails with the error message:

error: no suitable conversion function from "derived" to
"derived (*)()" exists


int main()
{
const base &r(derived());
derived dobject;
r(dobject);
}

I don't know if this is a bug or if there is some way in which a parameter
of type

derived()

can legitimately be intepreted as a parameter of type

derived(*)().

I can't find any support for that in the Standard. It, apparently,
allows an argument to a function to be another function, and there
is no conversion necessary, AFAICT.

If 'T' is a function, an lvalue of type T can be converted to an rvalue
of type "a pointer to T", which will be "a pointer to a function ...".
However, there is nothing in the Standard that says that in a function
declaration a declaration of an argument in case of a function shall be
interpreted as a pointer to a function.

Victor
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,733
Messages
2,569,440
Members
44,832
Latest member
GlennSmall

Latest Threads

Top