>This question is related to the FAQ's 21.2 and 21.5, but since it
>involves neither pointers to pointers nor the arrays-are-pointers
>feature that C++ inherited from C it seems that it is sufficiently
>novel to be posted here.
>Consider the following code:
> A(int x_)
> x = x_;
> int x;
>class B : public A
> B() : A(1)
>class C : public A
> C() : A(2), y(3)
> int y;
>void f(A * a)
> *a = * new B(); // (1)
> C c;
> cerr << c.x << endl;
> cerr << c.y << endl;
>This program prints
>What I think happens is that the line marked (1) creates a new B, but
>then uses A's copy constructor to copy the object into *a. This means
>that the B gets sliced down to just the x=1. However, this gets
>copied into a C, since *a is actually c, so you end up with a C that
>could never have been created in a type-safe manner.
Conceptually correct. If you substitute "assignment operator" for
"copy constructor", then also technically correct. Nit, picked.
The problem you've run into is the general one that assignment via
an object reference (whether it's a C++ pointer or a C++ reference)
is not type-safe in the presence of polymorphism.
As an academic exercise it's amusing to think about what makes
assignment so special. Essentially it is that the object assigned
from must be the same type as the object assigned to. If you then
think of assignment as a virtual function you see that it (and any
other function with this parameter requirement) clashes with
polymorphism -- how C++ programmers manage without a clear view of
this is (and all discussion show that they do) is a great mystery!
Eiffel solves this problem *for assignment* by differentiating
between _expanded_ objects and _reference_ objects, and an object
must be one or the other, never both, never neither:
- Lives on the stack or as part of another object.
- Supports assignment.
- Prohibits reference creation.
- Is dynamically allocated.
- Prohibits assignment (to the object itself, but rules about
expanded and reference objects apply recursively to parts).
- Supports reference creation.
Java and C# use a restricted form of this solution (as in very
early Eiffel), where some built-in types are expanded (only),
and all other types are reference types (only). In fact, if
you look at the actual languages sans syntax then Java is
early --Eiffel++, and C# is --Java++. Well, in my opinion... ;-)
>To put it another way, the * type constructor seems covariant with
>respect to subtyping - X <: Y implies X* <: Y* when in fact that type
>constructor must be invariant if it is to be safe to assign through
Harumph. I sincerely dislike academic notation and most especially
the terms "variant" and "covariant" flung about with no reference to
IN or OUT -- the requirements are opposite in those cases, so not
stating which case applies makes for a nearly meaningless uttering.
That is, I sincerely dislike when it's just assumed that any reader
will share the same notation and terminology; it just is not so.
>Is this code legal?
Yes, I think so, but put it through a decent compiler to check that.
g++ gives a good indication. comeau even better.
>Is its behaviour defined?
> Should my compiler
>issue a warning here? (g++ -Wall -W passes the code with no
That's up to the compiler, IOW., it's a QOI issue.
Cheers, & hth.,
[ about comp.lang.c++.moderated. First time posters: do this! ]