Просто добавьте деструктор, который ничего не делает, может вызвать ошибку компиляции (вокруг std::move), почему?

Пока я учился std::moveЯ нашел странную проблему.

Если я добавлю только деструктор, который ничего не делает с идеальной программой, я получу ошибку компиляции.

#include <iostream>
using namespace std;

class M {
public:
  int database = 0;

  M &operator=(M &&other) {
    this->database = other.database;
    other.database = 0;
    return *this;
  }

  M(M &&other) { *this = std::move(other); }

  M(M &m) = default;
  M() = default;
  ~M() { /* free db */ }
};

class B {
public:
  M shouldMove;

  //~B(){}   //<---  ## Adding this line will cause compile error. ##
};

int main() {
  B b;
  B b2 = std::move(b); //## error at this line if the above line is added
  return 0;
}

Живой код: https://ideone.com/UTR9ob

Ошибка invalid initialization of non-const reference of type 'B&' from an rvalue of type 'std::remove_reference<B&>::type {aka B}',

Вопрос:

  • (1) Какие правила синтаксиса C++ обеспечивают это? Другими словами, что означает ошибка?
  • (2) Если я хочу добавить деструктор, который почти ничего не делает (например, только печатает журнал отладки) в Bя действительно должен следовать правилу пяти вместо этого? Если нет, как заставить его скомпилировать? Следование правилу пяти только из-за этого, на мой взгляд, слишком утомительно и грязно.

Я думаю, что нулевое правило - это просто хорошая практика.

Однако из этого примера мне кажется, что это жесткое правило, что в случае нарушения я получу ошибку компиляции.

1 ответ

Решение

Неявно объявленный конструктор перемещения присутствует, только если класс не имеет объявленного пользователем деструктора. Поэтому ответ на вопрос 2. ДА.

Ответ на 1. состоит в том, что это жесткое правило, и его можно найти в пункте 12.8 стандарта:

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

  • X не имеет объявленного пользователем конструктора копирования,
  • X не имеет заявленного пользователем оператора копирования,
  • X не имеет объявленного пользователем оператора назначения перемещения,
  • X не имеет объявленного пользователем деструктора, и
  • конструктор перемещения не будет неявно определен как удаленный.

[Примечание: Когда конструктор перемещения не объявляется неявным образом или не предоставляется явно, выражения, которые в противном случае вызвали бы конструктор перемещения, могут вместо этого вызывать конструктор копирования. - конец примечания]

Лучший способ заставить это работать, это использовать что-то вроде интеллектуального указателя, то есть базовый класс или член, который определяет все пять специальных членов (и очень мало других), так что вам не нужно это делать. В этом случае целочисленный дескриптор эквивалентен std::unique_pointer должно хорошо работать. Однако имейте в виду, что базы данных, как и файлы, могут иметь ошибки при закрытии, поэтому стандартная семантика деструктора без выбрасывания не охватывает все случаи.

Другие вопросы по тегам