Наследование конструкторов копирования и перемещения

Как происходит наследование конструкторов копирования и перемещения?

class Parent {
public:
    Parent() = default;
    Parent(const Parent& other) {
        cout << "Копирования" << endl;
    }
    Parent(Parent&& other) {
        cout << "Перемещения" << endl;
    }
};

class Child : public Parent {
public:
};

int main() {
    Parent p;
    Child c;

    Parent p1(c);

    Child c1(c);
    Child c2(move(c));
}

Вывод:

Копирования
Копирования
Перемещения

Почему этот код работает без using Parent::Parent? Ведь конструкторы не наследуются.

class Parent {
public:
    Parent() = default;

    Parent(const Parent& other) {
        cout << "Копирования" << endl;
    }
    Parent(Parent&& other) {
        cout << "Перемещения" << endl;
    }
};

class Child : public Parent {
public:
    using Parent::Parent;
    Child(const Child& other) {
        cout << "Детский Копирования" << endl;
    }
};

int main() {
    Parent p;
    Child c;

    Parent p1(c);

    Child c1(c);
    Child c2(move(c));
}

Вывод:

Копирования
Детский Копирования
Детский Копирования

Почему не унаследовался конструктор перемещения? Я ведь не переопределил его внутри Child.

Также хочу отметить, что подобное поведение не относится к другим конструкторам, они ведут себя очевидно.


Ответы (1 шт):

Автор решения: AlexGlebe

Конструкторы копирования/перемещения и операторы присваивания не обладают свойством универсальности для наследуемого класса. Например, конструктор наследуемого класса сначала вызывает конструктор базового, затем инициализируются все новые элементы нового класса. И наследование для этих функций не предусмотрено. Так как у этих классов разное количество личных элементов, не говоря уже о том, что базовых классов тоже может быть много.
При отсутствии декларации конструкторов в классе создаются эти элементы компилятором по-умолчанию. А при декларации например одного конструктора копирования, конструктор перемещения по-умолчанию компилятором не создаётся. И программист должен сам написать все эти функции.

В первом примере :

class Child : public Parent {
public:
};

сначала вызывается конструктор предка Parent затем инициализируются личные поля класса Child. И фактически это выглядит как такое объявление :

class Child : public Parent {
public:
  Child() : Parent() {}
  Child(Child const & x) : Parent ( x ) {}
  Child(Child && x) : Parent ( std::move(x) ) {}
};

Вот такой класс :

class Child : public Parent {
public:
    using Parent::Parent;
} ;

будет использовать все конструкторы предка кроме копирования/перемещения + будут добавлены автоматически конструкторы копирования/перемещения.

Во втором примере :

class Child : public Parent {
public:
    using Parent::Parent;
    Child(const Child& other) {
        cout << "Детский Копирования" << endl;
    }
};

запись using Parent::Parent; создаёт собственные конструкторы для нового класса, используя вызов базового, кроме копирования/перемещения.

Объявление собственной функции копирования Child(const Child& other) отключает свойство компилятора и он не объявляет все эти функции сам, а программист должен сам все эти функции написать индивидуально. По-этому у вас не будет конструктора перемещения и операторов присваивания.

→ Ссылка