Почему неявно и явно удаленные конструкторы перемещения обрабатываются по-разному?

Чтообоснование позади разной обработки неявно и явно удаленных конструкторов перемещения в стандарте C ++ 11, в отношении неявной генерации конструкторов перемещения, содержащих / наследующих классы?

C ++ 14 / C ++ 17 что-нибудь изменить? (КромеDR1402 в C ++ 14)

Примечание: я понимаю, что происходит, я понимаю, что это в соответствии с правилами стандарта C ++ 11, я заинтересован в обосновании этих правил, которые подразумевают такое поведение (пожалуйста, убедитесь, что вы просто не повторяете, что это так это потому что стандарт так говорит)

Принять классExplicitDelete с явно удаленным движением ctor и явно дефолтным копированием ctor. Этот класс неmove constructible хотясовместимый Копирование ctor доступно, потому что разрешение перегрузки выбирает конструктор перемещения и завершается неудачно во время компиляции из-за его удаления.

Принять классImplicitDelete который содержит или наследует отExplicitDelete и больше ничего не делает. Этот класс будет иметь свой ctor перемещения, неявно объявленный как удаленный из-заC ++ 11 перемещает ctor правила, Тем не менее, этот класс все еще будетmove constructible через его копию ctor. (Связано ли это последнее утверждение с разрешениемDR1402?)

Тогда классImplicit содержащий / наследующий отImplicitDelete будет иметь совершенно прекрасный неявный сгенерированный конструктор перемещения, который вызываетImplicitDeleteКопия ctor.

Так в чем же причинаImplicit иметь возможность двигаться неявно иImplicitDelete не быть в состоянии двигаться безоговорочно?

На практике, еслиImplicit а такжеImplicitDelete есть некоторые сверхмощные подвижные элементы (подумайтеvector<string>), Я не вижу причин, по которымImplicit должен быть значительно вышеImplicitDelete в движении производительности.ImplicitDelete мог бы еще копироватьExplicitDelete от его неявного хода ctor - так же, какImplicit делает сImplicitDelete.

Мне это поведение кажется противоречивым. Я бы нашел более последовательным, если бы произошло одно из этих двух событий:

Компилятор обрабатывает как неявно, так и явно удаленные векторы перемещения:

ImplicitDelete становится неmove-constructible, какExplicitDeleteImplicitDeleteудаленный ход ctor приводит к удаленному неявному движению ctor вImplicit (так же, какExplicitDelete делает это сImplicitDelete)Implicit становится неmove-constructibleПодборкаstd::move строка совершенно не в моем примере кода

Или компилятор возвращается к копированию ctorтакже заExplicitDelete:

ExplicitDeleteКопируем конструктор во всехmoveс, как и дляImplicitDeleteImplicitDelete получает правильный неявный ход ctor(Implicit неизменен в этом сценарии)Выходные данные примера кода показывают, чтоExplicit член всегда перемещен.

Вот полностью рабочий пример:

#include <utility>
#include <iostream>
using namespace std;

struct Explicit {
    // prints whether the containing class's 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's copy and 1x Implicit's move
    return 0;
}

Ответы на вопрос(1)

Ваш ответ на вопрос