Является ли конструктор перемещения `= default` эквивалентным конструктору перемещения элемента?
Это
struct Example {
int a, b;
Example(int mA, int mB) : a{mA}, b{mB} { }
Example(const Example& mE) : a{mE.a}, b{mE.b} { }
Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
Example& operator=(const Example& mE) { a = mE.a; b = mE.b; return *this; }
Example& operator=(Example&& mE) { a = move(mE.a); b = move(mE.b); return *this; }
}
эквивалентно этому
struct Example {
int a, b;
Example(int mA, int mB) : a{mA}, b{mB} { }
Example(const Example& mE) = default;
Example(Example&& mE) = default;
Example& operator=(const Example& mE) = default;
Example& operator=(Example&& mE) = default;
}
?
Ответы
Ответ 1
Да, оба одинаковы.
Но
struct Example {
int a, b;
Example(int mA, int mB) : a{mA}, b{mB} { }
Example(const Example& mE) = default;
Example(Example&& mE) = default;
Example& operator=(const Example& mE) = default;
Example& operator=(Example&& mE) = default;
}
Эта версия позволит вам пропустить определение тела.
Однако, когда вы объявляете explicitly-defaulted-functions
:
вы должны следовать некоторым правилам,
8.4.2. Явно-дефолтные функции [dcl.fct.def.default]
Определение функции вида:
attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt = default ;
называется явно дефолтным определением. Функция, которая явно дефолтна, должна
-
- специальная функция-член,
-
имеют один и тот же объявленный тип функции (за исключением, возможно, отличающихся реф-квалификаторов, за исключением того, что в случае конструктора копирования или оператора присваивания копии тип параметра может быть "ссылкой на неконстантный T
", где T
- это имя класса функций-членов), как если бы оно было объявлено неявно,
-
не имеют аргументов по умолчанию.
Ответ 2
Да, конструктор перемещения по умолчанию будет выполнять поэтапное перемещение его базы и элементов, поэтому:
Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
эквивалентно:
Example(Example&& mE) = default;
мы можем это увидеть, перейдя в проект С++ 11. 12.8
Копирование и перемещение объектов класса, приведенных в пункте 13, в котором говорится (акцент мой на будущее)
Конструктор копирования/перемещения, который по умолчанию и не определен как удаленный неявно определяется, если он одуван (3.2) или когда он явно дефолт после его первого объявления. [Примечание: копия/перемещение конструктор неявно определяется, даже если реализация его odr-use (3.2, 12.2). -end note] [...]
и параграф 15, в котором говорится:
неявно определенный конструктор копирования/перемещения для неединичного класса X выполняет поэтапное копирование/перемещение своих баз и элементов. [ Заметка: скопированные или равные инициализаторы нестатических элементов данных игнорируются. См. Также пример в 12.6.2. -end note] Порядок инициализация такая же, как и порядок инициализации базиса и членов в определяемом пользователем конструкторе (см. 12.6.2). Пусть x - либо параметр конструктора или, для конструктора перемещения, xvalue, ссылаясь на параметр. Каждый базовый или нестатический элемент данных копируется/перемещается в соответствии с его типом:
- если элемент является массивом, каждый элемент инициализируется прямым образом с соответствующим подобъектом x;
- если элемент m имеет ссылочный тип rvalue T & &, он напрямую инициализируется static_cast (x.m);
- в противном случае база или элемент напрямую инициализируются с соответствующей базой или элементом x.
Субобъекты виртуального базового класса должны быть инициализированы только один раз неявно заданный конструктор копирования/перемещения (см. 12.6.2).
Ответ 3
Является ли конструктор перемещения по =default
эквивалентным членскому конструктору перемещения?
Да Обновление: ну не всегда. Посмотрите на этот пример:
#include <iostream>
struct nonmovable
{
nonmovable() = default;
nonmovable(const nonmovable &) = default;
nonmovable( nonmovable &&) = delete;
};
struct movable
{
movable() = default;
movable(const movable &) { std::cerr << "copy" << std::endl; }
movable( movable &&) { std::cerr << "move" << std::endl; }
};
struct has_nonmovable
{
movable a;
nonmovable b;
has_nonmovable() = default;
has_nonmovable(const has_nonmovable &) = default;
has_nonmovable( has_nonmovable &&) = default;
};
int main()
{
has_nonmovable c;
has_nonmovable d(std::move(c)); // prints copy
}
Это печатает:
copy
http://coliru.stacked-crooked.com/a/62c0a0aaec15b0eb
Вы объявили конструктор перемещения по умолчанию, но вместо перемещения происходит копирование. Зачем? Потому что, если у класса есть хотя бы один неподвижный член, то явно заданный по умолчанию конструктор перемещения неявно удаляется (такой каламбур). Поэтому, когда вы запускаете has_nonmovable d = std::move(c)
, конструктор копирования фактически вызывается, поскольку конструктор перемещения из has_nonmovable
удаляется (неявно), он просто не существует (даже если вы явно объявили конструктор перемещения выражение has_nonmovable(has_nonmovable &&) = default
).
Но если бы конструктор перемещения non_movable
не был объявлен, конструктор перемещения использовался бы для movable
(и для каждого члена, имеющего конструктор перемещения), а конструктор копирования использовался бы для nonmovable
(и для каждого члена, который не определяет конструктор ходов). Смотрите пример:
#include <iostream>
struct nonmovable
{
nonmovable() = default;
nonmovable(const nonmovable &) { std::cerr << "nonmovable::copy" << std::endl; }
//nonmovable( nonmovable &&) = delete;
};
struct movable
{
movable() = default;
movable(const movable &) { std::cerr << "movable::copy" << std::endl; }
movable( movable &&) { std::cerr << "movable::move" << std::endl; }
};
struct has_nonmovable
{
movable a;
nonmovable b;
has_nonmovable() = default;
has_nonmovable(const has_nonmovable &) = default;
has_nonmovable( has_nonmovable &&) = default;
};
int main()
{
has_nonmovable c;
has_nonmovable d(std::move(c));
}
Это печатает:
movable::move
nonmovable::copy
http://coliru.stacked-crooked.com/a/420cc6c80ddac407
Обновление: но если вы закомментируете строку has_nonmovable(has_nonmovable &&) = default;
, тогда копия будет использоваться для обоих участников: http://coliru.stacked-crooked.com/a/171fd0ce335327cd - печатает:
movable::copy
nonmovable::copy
Так что, вероятно, установка =default
везде имеет смысл. Это не значит, что ваши выражения перемещения всегда будут двигаться, но это повышает шансы на это.
Еще одно обновление: Но если закомментировать строку has_nonmovable(const has_nonmovable &) = default;
либо, то результатом будет:
movable::move
nonmovable::copy
Так что если вы хотите знать, что происходит в вашей программе, просто сделайте все самостоятельно: sigh:
Ответ 4
разделяют очень патологические случаи... ДА.
Чтобы быть более точным, вы также должны учитывать возможные базы Example
, имеющие одинаковые правила. Сначала основы - в порядке объявления - затем члены, всегда в порядке объявления.