Просто добавьте деструктор, который ничего не делает, может вызвать ошибку компиляции (вокруг 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
должно хорошо работать. Однако имейте в виду, что базы данных, как и файлы, могут иметь ошибки при закрытии, поэтому стандартная семантика деструктора без выбрасывания не охватывает все случаи.