Почему производный класс move конструктивен, когда базовый класс не является?

рассмотрим следующий пример:

#include <iostream>
#include <string>
#include <utility>

template <typename Base> struct Foo : public Base {
    using Base::Base;
};

struct Bar {
    Bar(const Bar&) { }
    Bar(Bar&&) = delete;
};

int main() {
    std::cout << std::is_move_constructible<Bar>::value << std::endl; // NO
    std::cout << std::is_move_constructible<Foo<Bar>>::value << std::endl; // YES. Why?!
}

почему компилятор генерирует конструктор перемещения, несмотря на то, что базовый класс не является конструктивным?

это в стандарте или это ошибка компилятора? Можно ли" идеально распространять " конструкцию перемещения из базового в производный класс?

2 ответов


потому что:

конструктор перемещения по умолчанию, который определен как удаленный, игнорируется разрешением перегрузки.

([класс.copy] / 11)

Barконструктор перемещения -явно удалены, так что Bar не может быть перемещен. Но!--2-->конструктор перемещения -неявно удалены после того, как неявно объявлен как дефолт, из-за того, что Bar элемент не может быть перемещен. Поэтому Foo<Bar> может перемещается с помощью конструктора copy.

Edit: я также забыл упомянуть важный факт, что объявление наследующего конструктора, такое как using Base::Base не наследует конструкторы default, copy или move, поэтому Foo<Bar> не имеет явно удаленного конструктора перемещения, унаследованного от Bar.


1. Поведение std::is_move_constructible

это ожидаемое поведение std:: is_move_constructible:

типы без конструктора перемещения, но с конструктором копирования, который принимает const T& аргументы, удовлетворить std::is_move_constructible.

что означает, что с конструктором копирования все еще можно построить T из ссылки rvalue T&&. И Foo<Bar> есть неявно объявленная копия конструктор.

2. Неявно объявленный конструктор перемещения Foo<Bar>

почему компилятор генерирует конструктор перемещения, несмотря на то, что базовый класс не является конструктивным?

фактически, конструктор перемещения Foo<Bar> определяется как удалены, но обратите внимание, что удаленный неявно объявленный конструктор перемещения игнорируется разрешением перегрузки.

в неявно объявленный или дефолтный конструктор перемещения для класса T is определено как удалено в любом из следующих значений true:

...
T has direct or virtual base class that cannot be moved (has deleted, inaccessible, or ambiguous move constructors); 
...

удаленный неявно объявленный конструктор перемещения игнорируется разрешением перегрузки (иначе это предотвратило бы инициализацию копирования из rvalue).

3. Различное поведение между Bar и Foo<Bar>

обратите внимание, что конструктор перемещения из Bar объявлен deleted явно и конструктор перемещения Foo<Bar> неявно объявлено и определено как deleted. Дело в том, что удаленный неявно объявленный конструктор перемещения игнорируется разрешением перегрузки, что позволяет перемещать конструкцию Foo<Bar> конструктор копирования. Но явно удаленный конструктор перемещения будет участвовать в разрешении перегрузки, означает при попытке переместить конструктор Bar будет выбран конструктор удаленного перемещения, затем программа плохо сформирована.

вот почему Foo<Bar> это перемещение конструктивно, но Bar нет.

стандарт имеет явное заявление об этом. 12.8$/11 копирование и перемещение объектов класса [класс.копировать]

конструктор перемещения по умолчанию, определенный как удаленный, игнорируется разрешением перегрузки ([over.матч], [конец.над.)] [ Примечание: в противном случае удаленный конструктор перемещения будет препятствовать инициализации из rvalue, который может использовать копии вместо конструктора. - конец Примечания ]