See the following code:
#include <iostream>
#include <chrono>
class Parent
{
public:
Parent() = default;
virtual ~Parent() = default;
Parent(const Parent& pr) : i{pr.i} {std::cout << "Parent copy constructor\n";}
Parent& operator=(const Parent& pr) {std::cout << "Parent copy assignment\n"; this->i = pr.i; return *this;}
Parent(Parent&& pr) : i{std::move(pr.i)} {std::cout << "Parent move constructor\n";}
Parent& operator=(Parent&& pr) {std::cout << "Parent move assignment\n"; this->i = std::move(pr.i); return *this;}
virtual void print_i_j() = 0;
int i = 10;
};
class Child : public Parent
{
public:
Child() = default;
Child(const Child& cr) : Parent{cr}, j{cr.j} {std::cout << "Child copy constructor\n";}
Child& operator=(const Child& cr) {std::cout << "Child copy assignment\n"; this->j = cr.j; return *this;}
Child(Child&& cr) : Parent{std::move(cr)}, j{std::move(cr.j)} {std::cout << "Child move constructor\n";}
Child& operator=(Child&& cr) {std::cout << "Child move assignment\n"; Parent::operator=(std::move(cr)); this->j = std::move(cr.j); return *this;}
void print_i_j() {std::cout << "i = "<< i << " j = " << j << std::endl;}
int j = 100;
};
int main(int argc, const char * argv[])
{
Child c;
c.i = 30;
c.j = 300;
c.print_i_j();
Child c2; // leave c2 with defaults (i=10, j=100)
Parent& p_ref = c2;
p_ref.print_i_j();
c2.j = 150;
p_ref.print_i_j();
p_ref = std::move(c); // (1)
p_ref.print_i_j(); // (2)
return 0;
}
When I run this I get:
i = 30 j = 300
i = 10 j = 100
i = 10 j = 150
Parent move assignment
i = 30 j = 150
As far as I can tell, as indicated in this output, i
changes as a result of moving an instance of the derived class into a reference to the parent class, but j
does not.
Is the result printed in (2) an indication that the move in (1) caused slicing? Or is some other behavior (or even undefined behavior) kicking in?
Can std::move cause slicing ...
No. std::move
doesn't cause slicing.
However, assignment into a base object causes slicing i.e. only the base is assigned and rest of the object is unaffected. This happens both when copy assigning as well as move assigning. Move assignment does however have an extra consideration: Not only is only the base of the left hand operand assigned (like in the case of copy), but also only the base of the right hand operand has been moved from.
Whether the base is assigned through a reference or not does not affect slicing unless the assignment operator is virtual (don't use virtual assignment operators though; they are not an easy / good solution).
Either:
In any case, make sure that the static type of the left hand operand is what you expect it to be when assigning.