0

I have a Programmer class which derives from a Person class, when I try to initialize a Programmer object with a Programmer object r-value, it doesn't use my Move Constructor, which is defined in the Programmer class, but instead uses one of the overloaded r-value constructors.

Here is the code which I am talking about:

int main() {

    Programmer test1{"Juan", 15, "Programmer", {"Leo", "Ty", "Ali"}, "Tesla"};
    Programmer test2{Programmer{"Leo", 15, "Programmer", {"Leo", "Ty", "Ali"}, "SpaceX"}};

    return 0;
}

As you can see on test2, I would expect that to call my Move Constructor (from what I have researched and from what I know, the Move Constructor should be called since I am passing a Programmer object r-value):

Programmer(Programmer &&source) noexcept : Person{std::move(source)}, company_name{std::move(source.company_name)} {}

but instead it calls one of my overloaded constructors which I specifically made for r-values (I don't know if that is good practise for performance driven programs):

Programmer(std::string &&name, int &&age, std::string &&profession, std::vector<std::string> &&friends, std::string &&company_name) : Person{std::move(name), std::move(age), std::move(profession), std::move(friends)}, company_name{std::move(company_name)} {}

Why is that the case? Could someone please explain?

Here is the whole context if you want to see:

#include <iostream>
#include <string>
#include <vector>

class Person {

protected:
    std::string name;
    int age;
    std::string profession;
    std::vector<std::string> friends;

public:
    Person() : name{}, age{}, friends{}, profession{} {}
    Person(const std::string &name) : name{name}, age{}, profession{}, friends{} {}
    Person(const std::string &name, const int &age) : name{name}, age{age}, profession{}, friends{} {}
    Person(const std::string &name, const int &age, const std::string &profession) : name{name}, age{age}, profession{profession}, friends{} {}
    Person(const std::string &name, const int &age, const std::string &profession, const std::vector<std::string> &friends) : name{name}, age{age}, profession{profession}, friends{friends} {}

    // R-values constructors
    Person(std::string &&name) : name{std::move(name)}, age{}, friends{} {}
    Person(std::string &&name, int &&age) : name{std::move(name)}, age{std::move(age)}, friends{} {}
    Person(std::string &&name, int &&age, std::string &&profession) : name{std::move(name)}, age{std::move(age)}, profession{std::move(profession)}, friends{} {}
    Person(std::string &&name, int &&age, std::string &&profession, std::vector<std::string> &&friends) : name{std::move(name)}, age{std::move(age)}, profession{std::move(profession)}, friends{std::move(friends)} {}

    // Copy constructor
    Person(const Person &source) : name{source.name}, age{source.age}, friends{source.friends} {}

    // Move Constructor
    Person(Person &&source) noexcept : name{std::move(source.name)}, age{std::move(source.age)}, friends{std::move(source.friends)} {}

    // Destructor
    ~Person() = default;
};

class Programmer : public Person {

protected:
    std::string company_name;

public:
    Programmer() : Person{}, company_name{} {}
    Programmer(const std::string &name) : Person{name}, company_name{} {}
    Programmer(const std::string &name, const int &age) : Person{name, age}, company_name{} {}
    Programmer(const std::string &name, const int &age, const std::string &profession) : Person{name, age, profession}, company_name{} {}
    Programmer(const std::string &name, const int &age, const std::string &profession, const std::vector<std::string> &friends) : Person{name, age, profession, friends}, company_name{} {}
    Programmer(const std::string &name, const int &age, const std::string &profession, const std::vector<std::string> &friends, const std::string &company_name) : Person{name, age, profession, friends}, company_name{company_name} {}

    // R-values constructors
    Programmer(std::string &&name) : Person{std::move(name)} {}
    Programmer(std::string &&name, int &&age) : Person{std::move(name), std::move(age)} {}
    Programmer(std::string &&name, int &&age, std::string &&profession) : Person{std::move(name), std::move(age), std::move(profession)} {}
    Programmer(std::string &&name, int &&age, std::string &&profession, std::vector<std::string> &&friends) : Person{std::move(name), std::move(age), std::move(profession), std::move(friends)} {}
    Programmer(std::string &&name, int &&age, std::string &&profession, std::vector<std::string> &&friends, std::string &&company_name) : Person{std::move(name), std::move(age), std::move(profession), std::move(friends)}, company_name{std::move(company_name)} {}

    // Copy constructor
    Programmer(const Programmer &source) : Person{source}, company_name{source.company_name} {}

    // Move constructor
    Programmer(Programmer &&source) noexcept : Person{std::move(source)}, company_name{std::move(source.company_name)} {}

    // Destructor
    ~Programmer() = default;

};

int main() {

    Programmer test1{"Juan", 15, "Programmer", {"Leo", "Ty", "Ali"}, "Tesla"};
    Programmer test2{Programmer{"Leo", 15, "Programmer", {"Leo", "Ty", "Ali"}, "SpaceX"}};

    return 0;
}
Krapnix
  • 257
  • 2
  • 8
  • What I see, after stepping with gdb is that the call to "one of my overloaded constructors" is to construct `Programmer{"Leo"...}`, and when it returns, it appears that RVO eliminates the move entirely. – Sam Varshavchik Jan 10 '21 at 02:15
  • TL;DR of the dupe: the compiler/language is smart enough to know the temporary doesn't need to exist and just directly constructs the object. this is a "feature". – NathanOliver Jan 10 '21 at 02:19
  • @NathanOliver are there any good sources which will explain this in detail? Nvm I found the source as it is given to me by the "closed" and its recommendation – Krapnix Jan 10 '21 at 02:31
  • @SamVarshavchik Ok thank you, for putting me in the right direction on what it is. – Krapnix Jan 10 '21 at 02:33

0 Answers0