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;
}