Is the object slicing that occurs when assigning a derived object through a base class reference (without a virtual operator=
) a well-defined operation? I.e., is it guaranteed by the standard to leave the derived parts of the object untouched?
In many questions regarding object slicing here and elsewhere, an example like the following is given:
struct Base{
int x;
Base(int xx) :x(xx) {}
virtual void print() const {std::cout << "Base("<<x<<")\n";}
};
struct Derived : Base{
int y;
Derived(int xx, int yy ) :Base(xx),y(yy){}
void print() const {std::cout << "Derived("<<x<<","<<y<<")\n";}
};
int main()
{
Derived d1{1,2};
Derived d2{3,4};
Base& br = d1;
br = d2; // assign a Derived through a Base&
br.print(); // prints Derived(3,2)
}
The example is intended to show that when assigning through a Base&
it only assigns the Base
class members and leaves the members in Derived
untouched.
Building the above example with -fsanitize=undefined
does not complain, and it produces the expected output on all systems I've run it.
The question is, is this guaranteed by the standard? In my understanding it is, as Base::operator=
cannot write outside the Base
part of the object, and the Derived
part of the object (here int y
) cannot have any overlap with the Base
part.
Is there some corner case I am missing? (There are of course many ways having an inconsistent object can lead to undefined behaviour, but my question is limited to what happens during the assignment operation.)
Base& br = d1;
This creates a reference to a Base
object. This will now be referencing a Base
object that's just as well-formed as any other Base
object (in a well-formed C++ program). In all respects, the referenced Base
object is identical to all other Base
objects that may or may not exist. This object can be assigned to just like any other Base
object can be assigned to, and the exact same thing will happen to this object as to any other Base
object that gets assigned to. The End.
Objects that are base objects of other objects are not any different than objects that are not, when viewed through the prism of those objects alone. They are well-formed objects of their own, and are just like any other Base
object (ignoring things like virtual methods here). This is a fundamental concept in C++.
It would be rather inconvenient if assigning to a Base object suddenly "writes" somewhere else. It would be rather difficult to accomplish anything in C++, in that case. Of course, it goes without saying that one can always overload operator=
, and then the sky's the limit. But from a practical point, as long as the =
operator works the way it's expected to work, assigning to a Base
object won't do anything to anything that's not a part of that Base
object.