Home > database >  Child class invoking parent copy assignment instead of move assignment?
Child class invoking parent copy assignment instead of move assignment?

Time:08-11

Hi i wrote the code bellow, I have a parent class (Vehicule) and a child class (Car), i'm trying to study the behavior of Move/Copy assignment with inheritance. However there is something i don't understand quite well: The move assignment of the child class invoke the move assignment of the parent class, to take care of the parent class part of the object (slicing)

Car &Car::operator=(Car &&rhs)
{
    if (this != &rhs)
    {
        Vehicule::operator=(rhs);
        this->production = rhs.production;
        std::cout << "Car Move Assignment for: " << this->name << std::endl;
    }
    return *this;
}

However as my object is an R-Value, I don't understand why my child class calls the parent Copy assignment instead of The Move assignment when am creating the object;

Car audi;
audi= Car{"Audi_A3", 2023};

I used the debugger and this line of code:

Vehicule::operator=(rhs);

takes me to the parent class copy assignment

Vehicule &Vehicule::operator=(const Vehicule &rhs)

instead of :

Vehicule &Vehicule::operator=(Vehicule &&rhs)
#include <iostream>
#include <string>
#include <ostream>
#include <vector>

class Vehicule
{
protected:
    std::string name;

public:
    Vehicule();                               // No args constructor
    Vehicule(std::string name);               // overloaded constructor
    Vehicule(const Vehicule &source);         // copy constructor
    Vehicule(Vehicule &&source);              // move constructor
    Vehicule &operator=(const Vehicule &rhs); // copy assignment
    Vehicule &operator=(Vehicule &&rhs);      // move assignment
    ~Vehicule();
};

Vehicule::Vehicule() : name{"Vehicule"} {}
Vehicule::Vehicule(std::string name) : name{name} {}
Vehicule::~Vehicule() {}

// Copy constructor
Vehicule::Vehicule(const Vehicule &source) : name{source.name}
{
    std::cout << "Vehicule Copy constructor for: " << name << std::endl;
}

// Move constructor
Vehicule::Vehicule(Vehicule &&source) : name{source.name}
{
    std::cout << "Vehicule Move constructor for: " << source.name << std::endl;
}
// copy assignment
Vehicule &Vehicule::operator=(const Vehicule &rhs)
{
    if (this != &rhs)
    {
        this->name = rhs.name;
        std::cout << "Vehicule Copy assignment for: " << this->name << std::endl;
    }
    return *this;
}

// Move assignment
Vehicule &Vehicule::operator=(Vehicule &&rhs)
{
    if (this != &rhs)
    {
        this->name = rhs.name;
        std::cout << "Vehicule Move assignment for: " << this->name << std::endl;
    }
    return *this;
}

class Car : public Vehicule
{
    friend std::ostream &operator<<(std::ostream &os, const Car &car);
    friend std::istream &operator>>(std::istream &is, Car &car);

private:
    int production;

public:
    Car();                                 // No args constructor
    Car(std::string name, int production); // overloaded constructor
    ~Car();
    Car(const Car &source);         // Copy constructor
    Car(Car &&source);              // Move constructor
    Car &operator=(const Car &rhs); // Copy assignment
    Car &operator=(Car &&rhs);      // Move assignment
    std::string get_name() { std::cout << name << " ." << production << std::endl; };
};

Car::Car() : production{2000} {}

Car::Car(std::string name, int production) : Vehicule{name}, production{production} {}

Car::~Car() {}

// copy constructor
Car::Car(const Car &source) : Vehicule{source.name}, production{source.production}
{
    std::cout << "Car Copy Constructor for: " << name << std::endl;
}

// Move constructor
Car::Car(Car &&source) : Vehicule(source.name), production{source.production}
{
    std::cout << "Car Move Constructor for: " << name << std::endl;
}

Car &Car::operator=(const Car &rhs)
{
    if (this != &rhs)
    {
        Vehicule::operator=(rhs);
        this->production = rhs.production;
        std::cout << "Car Copy Assignment for: " << this->name << std::endl;
    }
    return *this;
}

Car &Car::operator=(Car &&rhs)
{
    if (this != &rhs)
    {
        Vehicule::operator=(rhs);
        this->production = rhs.production;
        std::cout << "Car Move Assignment for: " << this->name << std::endl;
    }
    return *this;
}
std::ostream
    &
    operator<<(std::ostream &os, const Car &car)
{
    os << car.name << " | " << car.production;
    return os;
}
std::istream &operator>>(std::istream &is, Car &car)
{
    std::cout << "Enter car and production: ";
    std::cin >> car.name >> car.production;
    return is;
}

int main(int argc, char const *argv[])
{
    // Car Move assignment & Vehicule move assignment.
    Car audi;
    audi= Car{"Audi_A3", 2023};

    return 0;
}

// OUTPUT 
// Vehicule Copy assignment for: Audi_A3
// Car Move Assignment for: Audi_A3

CodePudding user response:

Vehicule::operator=(rhs);

Here, rhs is an lvalue, and this results in copy-assignment getting called. Making it an rvalue is required in order to invoke the move-assignment operator:

Vehicule::operator=(std::move(rhs));

CodePudding user response:

However as my object is an R-Value

The object being passed to your Car's operator is indeed an rvalue, and it is being passed in via a reference to an rvalue. But, the reference itself is an lvalue (it has a name assigned to it * - rhs).

* Rule of thumb - if something has a name, it is an lvalue.

I don't understand why my child class calls the parent Copy assignment instead of The Move assignment when am creating the object;

Because the parameter you are passing to the parent's operator is an lvalue, not an rvalue. A move assignment operator does not take an lvalue as input, but a copy assignment operator does.

To invoke the parent's move assignment operator, you need to cast the rhs parameter from an lvalue to an rvalue. The standard library has a function for that exact purpose: std::move()

Car &Car::operator=(Car &&rhs)
{
    if (this != &rhs)
    {
        Vehicule::operator=(std::move(rhs)); // <--
        this->production = rhs.production;
        std::cout << "Car Move Assignment for: " << this->name << std::endl;
    }
    return *this;
}
  • Related