Конструктор Move исчезает в производном классе при добавлении пользовательского деструктора - PullRequest
3 голосов
/ 31 января 2020

У меня есть базовый класс только для перемещения и производный, который наследует конструкторы базы. Я хотел бы дать Derived собственный деструктор, но когда я это сделаю, он больше не наследует конструктор перемещения Base. Очень таинственный Что происходит?

Godbolt

// move-only
struct Base {
    Base() = default;
    Base(Base const &) = delete;
    Base(Base &&) {}
};

struct Derived : public Base {
    using Base::Base;

    // remove this and it all works
    ~Derived() { /* ... */ }
};

int main() {
    Base b;
    // works
    Base b2 = std::move(b);

    Derived d;
    // fails
    Derived d2 = std::move(d);
}

Ответы [ 3 ]

2 голосов
/ 31 января 2020

Конструкторы перемещения генерируются при определенных c обстоятельствах.

https://en.wikipedia.org/wiki/Special_member_functions

При создании деструктора вы остановили генерацию конструктора перемещения компилятором.

Кроме того, создайте виртуальный базовый деструктор, если у вас его нет. По умолчанию, если не нужно делать ничего особенного. То же самое с вашим конструктором перемещения Base, только не оставляйте его пустым, объявите его по умолчанию. Вы используете =delete, также используйте =default.

2 голосов
/ 31 января 2020

Конструктор перемещения не наследуется с using Base::Base; так, как вам кажется, потому что конструктор перемещения в Base не имеет сигнатуры, которую имел бы конструктор перемещения в Derived. Первый получает Base&&, второй - Derived&&.

Затем в Derived вы объявляете деструктор. Это запрещает неявное объявление конструктора перемещения для Derived. Таким образом, в Derived.

отсутствует конструктор перемещения. Затем компилятор возвращается к неявно сгенерированному конструктору копирования Derived для Derived d2 = std::move(d);. Но это определяется как удаленный, потому что базовый класс Derived не может копироваться. (Вы вручную удалили конструктор копирования Base.)

В разрешении перегрузки конструктор удаленных копий выбирается вместо унаследованного конструктора базовых классов Base(Base&&) (хотя значение Derived может связываться с Base&&) поскольку для последнего требуется последовательность преобразования, которая не считается точным соответствием , а привязка к const Derived& считается точным совпадением с целью разрешения перегрузки.

Также есть предложенная формулировка для разрешения CWG, проблема 2356 , которая исключит унаследованный Base конструктор перемещения от участия в разрешении перегрузки вообще. (Из того, что я могу сказать, это то, что компилятор уже реализует.)

Если у вас нет веской причины для объявления деструктора, не делайте этого. Если у вас есть причина, вам нужно снова выполнить операции перемещения по умолчанию, как вы это делали для конструктора перемещения в Base. (Возможно, вы захотите по умолчанию использовать оператор присваивания перемещения, если классы должны быть назначаемыми.)

Если вы намереваетесь использовать иерархию классов полиморфно, вы должны объявить виртуальный (дефолтный) деструктор в полиморфизме c base, но вам не нужно объявлять деструктор в производных классах.

1 голос
/ 31 января 2020

Унаследованный конструктор перемещения не имеет подписи для производного класса.

В первом случае без явно объявленного деструктора компилятор неявно объявляет конструктор перемещения по умолчанию для производного класса.

Во втором случае, когда деструктор объявлен явно, конструктор перемещения не объявлен неявно компилятором.

Из стандарта C ++ 17 (15.8.1 конструкторы копирования / перемещения)

8 Если определение класса X явно не объявляет конструктор перемещения, неявный неявно будет объявлен как дефолтный, если и только если

(8.1) X не имеет объявленной пользователем копии конструктор,

(8.2) X не имеет объявленного пользователем оператора назначения копирования,

- (8.3) X не имеет объявленного пользователем оператора назначения перемещения и

> - (8.4) X не имеет объявленного пользователем деструктора.

Но в любом случае конструктор перемещения Базовый класс не является конструктором перемещения производного класса из-за разных сигнатур.

...