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

?

4 65

4 ответа:

да оба одинаковые.

но

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.ПКТ.защита.по умолчанию]

определение функции формы:

  attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt = default ;

называется явно-по умолчанию определение. Один функция, которая явно по умолчанию должна

  • быть специальной функцией-членом,

  • имеют один и тот же объявленный тип функции (за исключением, возможно, отличающихся ref-квалификаторы и за исключением того, что в случае конструктора копирования или оператора присваивания копирования тип параметра может быть "ссылка на неконст T", где T - это имя класса функции-члена), как если бы оно было неявно объявлено,

  • нет аргументов по умолчанию.

Да, конструктор перемещения по умолчанию будет выполнять перемещение по элементам своей базы и членов, поэтому:

Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }

эквивалентно:

Example(Example&& mE)                 = default;

мы можем увидеть это, перейдя к проект стандарта C++11 раздел 12.8копирование и перемещение объектов класса абзац 13 где сказано (акцент мой идет вперед):

конструктор копирования / перемещения, который по умолчанию не определен как удаленный неявно определенными если это odrused (3.2) или, когда это явно дефолт после его первого объявления. [ Примечание: копирование / перемещение конструктор неявно определен, даже если реализация elided его Усо-использовать (3.2, 12.2). -конец Примечание ][...]

и пункта 15 он говорит:

The неявно определенный конструктор копирования/перемещения для несоюзного класса X выполняет memberwise копирование / перемещение его баз и членов. [ Отмечать: бандаж или равно инициализатор нестатических членов данных игнорируются. См. также пример в 12.6.2. -конец Примечание ] порядок инициализация такая же, как и порядок инициализации баз и элементы в определяемом пользователем конструкторе (см. 12.6.2). Пусть x быть параметр конструктора или конструктора перемещения, в xvalue, ссылающийся на параметр. Каждый базовый или нестатический элемент данных копируется / перемещается таким образом соответствующий его типу:

  • если элемент является массивом, каждый элемент непосредственно инициализируется соответствующим подобъектом x;
  • если элемент m имеет ссылочный тип rvalue T&&, он инициализируется напрямую с помощью static_cast (x. m);
  • в противном случае база или элемент инициализируется напрямую с помощью соответствующей базы или элемента x.

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

это =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

так что если вы хотите знать, что происходит в вашей программе, просто делать все по себе: вздох:

кроме совсем патологических случаев ... ДА.

если быть более точным, вы также должны рассмотреть возможные основания Example может быть, с точно такими же правилами. Сначала базы-в порядке объявления-затем члены, всегда в порядке объявления.