Абстрактные классы и семантика перемещения

В соответствии с "Правилом пятерки", когда я объявляю одну из: операцию копирования или перемещения или деструктора, я должен написать их все, потому что компилятор не создает их (некоторые из них) для меня. Но если мой класс (A) происходит от абстрактного класса с виртуальным деструктором, означает ли это, что деструктор в классе A будет считаться "определяемым пользователем"? Как следствие, перемещение семантики не будет работать с объектами этого класса A, потому что компилятор не будет генерировать для меня конструктор перемещения?

struct Interface {
    virtual void myFunction() = 0;
    virtual ~Interface() {};
};

struct A : public Interface {
    void myFunction() override {};
};

Ответы

Ответ 1

В [class.copy]:

Если определение класса X явно не объявляет конструктор перемещения, оно будет объявлено неявно как дефолт, если и только если (9.1) - X не имеет объявленного пользователем конструктора копирования,
(9.2) - X не имеет объявленного пользователем оператора назначения копирования,
(9.3) - X не имеет объявленного пользователем оператора назначения перемещения и (9.4) - X не имеет объявленного пользователем деструктора.
[Примечание: когда конструктор перемещения неявно не объявлен или явно указан, выражения, которые в противном случае мог бы вызвать конструктор перемещения, может вместо этого вызвать конструктор копирования. -end note]

Interface имеет объявленный пользователем деструктор, поэтому конструктор перемещения Interface не будет объявлен неявным образом как дефолт. Но A не соответствует ни одной из этих точек пули, поэтому будет иметь неявный конструктор перемещения, который по умолчанию. A(A&& ) просто скопирует часть Interface в соответствии с примечанием. Мы можем проверить это, добавив члены в Interface и A:

#include <iostream>

template <char c>
struct Obj {
    Obj() { std::cout << "default ctor" << c << "\n"; }  
    Obj(const Obj&) { std::cout << "copy ctor" << c << "\n"; }
    Obj(Obj&&) { std::cout << "move ctor" << c << "\n"; }
};

struct Interface {
    virtual void myFunction() = 0;
    virtual ~Interface() {};

    Obj<'I'> base;
};

struct A : public Interface {
    void myFunction() override {};

    Obj<'A'> derived;
};

int main() {
    A a1;                      // default I, default A
    std::cout << "---\n";
    A a2(std::move(a1));       // copy I, move A
    std::cout << "---\n";
    A a3(a2);                  // copy I, copy A
}