Что такое обоснование за разным рассмотрением неявно и явно удаленных конструкторов перемещения в стандарте С++ 11 в отношении неявной генерации конструкторов перемещения содержащих/наследующих классов?
Делает ли С++ 14/С++ 17 что-либо менять? (За исключением DR1402 в С++ 14)
Примечание: я понимаю, что происходит, я понимаю, что это согласно стандартным правилам С++ 11, меня интересует обоснование этих правил, которые подразумевают это поведение (пожалуйста, не просто повторите, что это так оно и есть, потому что стандарт говорит так).
Предположим, что класс ExplicitDelete
с явно удаленным движением ctor и явно дефолтной копией ctor. Этот класс не является move constructible
, хотя совместимая копия ctor доступна, потому что разрешение перегрузки выбирает конструктор перемещения и выходит из строя во время компиляции из-за его удаления.
Предположим, что класс ImplicitDelete
содержит или наследует от ExplicitDelete
и ничего не делает. Этот класс будет иметь свое движение, которое неявно объявляется удаленным из-за С++ 11 move ctor rules. Однако этот класс по-прежнему будет move constructible
через его копию ctor. (Это последнее утверждение связано с разрешением DR1402?)
Тогда класс Implicit
, содержащий/наследующий от ImplicitDelete
, будет иметь совершенно прекрасный неявный конструктор перемещения, сгенерированный, который вызывает ImplicitDelete
copy ctor.
Итак, в чем же причина, позволяющая Implicit
иметь возможность двигаться неявно и ImplicitDelete
не иметь возможности двигаться неявно?
На практике, если Implicit
и ImplicitDelete
имеют некоторые сверхмощные подвижные элементы (думаю, vector<string>
), я не вижу причин, чтобы Implicit
был значительно превосходен для ImplicitDelete
в движении. ImplicitDelete
все еще может копировать ExplicitDelete
из своего неявного перемещения ctor — точно так же, как Implicit
делает с ImplicitDelete
.
Для меня это поведение кажется непоследовательным. Я бы счел это более последовательным, если бы произошла одна из этих двух вещей:
-
Компилятор обрабатывает как неявно, так и явно удаленные переменные перемещения:
-
ImplicitDelete
становится неmove-constructible
, какExplicitDelete
-
ImplicitDelete
удаленное перемещение ctor приводит к удаленному неявному перемещению ctor вImplicit
(таким же образом, чтоExplicitDelete
делает это сImplicitDelete
) -
Implicit
становится неmove-constructible
- Компиляция строки
std::move
полностью не работает в моем примере кода
-
-
Или компилятор возвращается к копированию ctor также для
ExplicitDelete
:-
ExplicitDelete
Конструктор копирования вызывается во всехmove
s, как и дляImplicitDelete
-
ImplicitDelete
получает правильное неявное перемещение ctor - (
Implicit
в этом сценарии не изменяется) - Вывод образца кода указывает, что элемент
Explicit
всегда перемещается.
-
Вот полный рабочий пример:
#include <utility>
#include <iostream>
using namespace std;
struct Explicit {
// prints whether the containing class move or copy constructor was called
// in practice this would be the expensive vector<string>
string owner;
Explicit(string owner) : owner(owner) {};
Explicit(const Explicit& o) { cout << o.owner << " is actually copying\n"; }
Explicit(Explicit&& o) noexcept { cout << o.owner << " is moving\n"; }
};
struct ExplicitDelete {
ExplicitDelete() = default;
ExplicitDelete(const ExplicitDelete&) = default;
ExplicitDelete(ExplicitDelete&&) noexcept = delete;
};
struct ImplicitDelete : ExplicitDelete {
Explicit exp{"ImplicitDelete"};
};
struct Implicit : ImplicitDelete {
Explicit exp{"Implicit"};
};
int main() {
ImplicitDelete id1;
ImplicitDelete id2(move(id1)); // expect copy call
Implicit i1;
Implicit i2(move(i1)); // expect 1x ImplicitDelete copy and 1x Implicit move
return 0;
}