Почему производный класс 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, который может использовать копии вместо конструктора. - конец Примечания ]