Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can std::move cause slicing when moving into l-value reference?

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?

like image 954
ToddR Avatar asked Dec 07 '22 12:12

ToddR


1 Answers

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:

  • Make sure that derived classes can deal with assigned / moved from base sub objects (i.e. there should be no class invariants that could be violated by such assignment).
  • Or make base unassignable (maybe make assignment protected).
  • Or make base inaccessible (protected or private)

In any case, make sure that the static type of the left hand operand is what you expect it to be when assigning.

like image 170
eerorika Avatar answered May 12 '23 13:05

eerorika