Подтвердить что ты не робот

Является ли конструктор перемещения `= 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;
}

?

4b9b3361

Ответ 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, имеющие одинаковые правила. Сначала основы - в порядке объявления - затем члены, всегда в порядке объявления.