Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Почему нет присваивания / конструктора перемещения по умолчанию?

Автор вопроса: Viktor SehrОтветов: 4

Я простой программист. Переменные-члены моего класса чаще всего состоят из POD-типов и STL-контейнеров. Из-за этого мне редко приходится писать операторы присваивания или конструкторы копирования, поскольку они реализованы по умолчанию.

Добавьте к этому, если я использую std::move для неподвижных объектов, он использует оператор присваивания, что означает, что std::move совершенно безопасен.

Поскольку я простой программист, я хотел бы воспользоваться возможностями перемещения, не добавляя конструктор перемещения / оператор присваивания к каждому классу, который я пишу, поскольку компилятор может просто реализовать их как "this->member1_ = std::move(other.member1_);..."

Но это не так (по крайней мере, в Visual 2010), есть ли для этого какая-то особая причина?

Важнее; есть ли способ обойти это?

Обновление:. Если вы посмотрите на ответ GManNickG, он предоставляет отличный макрос для этого. И если вы не знали, если вы реализуете семантику перемещения, вы можете удалить функцию-член подкачки.

  • вы знаете, что можете заставить компилятор сгенерировать ctor перемещения по умолчанию aaronman 05.09.2013
  • std :: move не выполняет перемещение, он просто преобразует l-значение в r-значение. Перемещение по-прежнему выполняется конструктором перемещения. Owen Delahoy 01.10.2015
  • Вы говорите о MyClass::MyClass(Myclass &&) = default;? Sandburg 03.09.2019
  • Да, в наше время :) Viktor Sehr 23.03.2020

c++c++11move-semantics

Ответы

Ответ 1 · рейтинг 76 · James McNellis

Неявная генерация конструкторов перемещения и операторов присваивания была спорной, и в последние проекты стандарта C ++ были внесены серьезные изменения, поэтому доступные в настоящее время компиляторы, вероятно, будут вести себя иначе в отношении неявной генерации.

Подробнее об истории проблемы см. в документах WG21 2010 г. list и найдите mov

Текущая спецификация (N3225, от ноября) гласит (N3225 12.8 / 8):

Если в определении класса X явно не объявляется конструктор перемещения, он будет неявно объявлен как конструктор по умолчанию тогда и только тогда, когда

  • X не имеет конструктора копирования, объявленного пользователем, и

  • X не имеет объявленного пользователем оператора присваивания копии,

  • X не имеет объявленного пользователем оператора присваивания перемещения,

  • X не имеет деструктора, объявленного пользователем, и

  • конструктор перемещения не будет неявно определен как удаленный.

В 12.8 / 22 есть аналогичный язык, определяющий, когда оператор присваивания перемещения неявно объявляется как заданный по умолчанию. Вы можете найти полный список изменений, внесенных для поддержки текущей спецификации неявной генерации перемещений, в N3203: ужесточение условий для генерации неявных ходов, которое в значительной степени основывалось на одном из решений, предложенных в статье Бьярна Страуструпа N3201: Продолжаем прямо.

Ответ 2 · рейтинг 13 · Jerry Coffin

Неявно сгенерированные конструкторы перемещения рассматриваются в качестве стандарта, но могут быть опасными. См. анализ Дэйва Абрахамса.

В конце концов, однако, стандарт включал неявную генерацию конструкторов перемещения и операторов присваивания перемещения, хотя и с довольно существенным списком ограничений:

Если определение класса X явно не объявляет конструктор перемещения, он будет неявно объявлен как конструктор по умолчанию, если и только если
- X не имеет конструктора копии, объявленного пользователем,
- X не имеет объявленный пользователем оператор присваивания копии,
- X не имеет объявленного пользователем оператора присваивания перемещения,
- X не имеет объявленного пользователем деструктора, и
- конструктор перемещения не будет определяться неявно как удалено.

Однако это еще не все, что касается истории. Ctor может быть объявлен, но все же определен как удаленный:

Неявно объявленный конструктор копирования / перемещения является встроенным публичным членом своего класса. Конструктор копирования / перемещения по умолчанию для класса X определяется как удаленный (8.4.3), если X имеет:

- вариантный член с нетривиальным соответствующим конструктором, а X - класс, подобный объединению,
- нестатический член данных типа M (или его массив), который не может быть скопирован / перемещен из-за разрешения перегрузки (13.3 ), применительно к соответствующему конструктору M, приводит к неоднозначности или к функции, которая удалена или недоступна из конструктора по умолчанию,
- прямой или виртуальный базовый класс B, который не может быть скопирован / перемещен из-за разрешения перегрузки (13.3), применительно к соответствующему конструктору B приводит к неоднозначности или к функции, которая удалена или недоступна из конструктора по умолчанию,
- любой прямой или виртуальный базовый класс или нестатический член данных типа с удаленным деструктором или недоступен из конструктора по умолчанию,
- для конструктора копирования, нестатического члена данных ссылочного типа rvalue или
- для конструктора перемещения, нестатического члена данных или прямого или виртуального базового класса с тип, не имеющий конструкции перемещения r и не копируется тривиально.

Ответ 3 · рейтинг 8 · Community

(а пока работаю над дурацким макросом ...)

Да, я тоже пошел по этому пути. Вот ваш макрос:

// detail/move_default.hpp
#ifndef UTILITY_DETAIL_MOVE_DEFAULT_HPP
#define UTILITY_DETAIL_MOVE_DEFAULT_HPP

#include <boost/preprocessor.hpp>

#define UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE(pR, pData, pBase) pBase(std::move(pOther))
#define UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE(pR, pData, pBase) pBase::operator=(std::move(pOther));

#define UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR(pR, pData, pMember) pMember(std::move(pOther.pMember))
#define UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT(pR, pData, pMember) pMember = std::move(pOther.pMember);

#define UTILITY_MOVE_DEFAULT_DETAIL(pT, pBases, pMembers)                                               \
        pT(pT&& pOther) :                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE, BOOST_PP_EMPTY, pBases))                      \
        ,                                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR, BOOST_PP_EMPTY, pMembers))                         \
        {}                                                                                              \
                                                                                                        \
        pT& operator=(pT&& pOther)                                                                      \
        {                                                                                               \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE, BOOST_PP_EMPTY, pBases)  \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT, BOOST_PP_EMPTY, pMembers)     \
                                                                                                        \
            return *this;                                                                               \
        }

#define UTILITY_MOVE_DEFAULT_BASES_DETAIL(pT, pBases)                                                   \
        pT(pT&& pOther) :                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE, BOOST_PP_EMPTY, pBases))                      \
        {}                                                                                              \
                                                                                                        \
        pT& operator=(pT&& pOther)                                                                      \
        {                                                                                               \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE, BOOST_PP_EMPTY, pBases)  \
                                                                                                        \
            return *this;                                                                               \
        }

#define UTILITY_MOVE_DEFAULT_MEMBERS_DETAIL(pT, pMembers)                                               \
        pT(pT&& pOther) :                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR, BOOST_PP_EMPTY, pMembers))                         \
        {}                                                                                              \
                                                                                                        \
        pT& operator=(pT&& pOther)                                                                      \
        {                                                                                               \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT, BOOST_PP_EMPTY, pMembers)     \
                                                                                                        \
            return *this;                                                                               \
        }

#endif

​

// move_default.hpp
#ifndef UTILITY_MOVE_DEFAULT_HPP
#define UTILITY_MOVE_DEFAULT_HPP

#include "utility/detail/move_default.hpp"

// move bases and members
#define UTILITY_MOVE_DEFAULT(pT, pBases, pMembers) UTILITY_MOVE_DEFAULT_DETAIL(pT, pBases, pMembers)

// base only version
#define UTILITY_MOVE_DEFAULT_BASES(pT, pBases) UTILITY_MOVE_DEFAULT_BASES_DETAIL(pT, pBases)

// member only version
#define UTILITY_MOVE_DEFAULT_MEMBERS(pT, pMembers) UTILITY_MOVE_DEFAULT_MEMBERS_DETAIL(pT, pMembers)

#endif

(Я удалил настоящие комментарии, длинные и документальные.)

Вы указываете базы и / или члены в своем классе как список препроцессора, например:

#include "move_default.hpp"

struct foo
{
    UTILITY_MOVE_DEFAULT_MEMBERS(foo, (x)(str));

    int x;
    std::string str;
};

struct bar : foo, baz
{
    UTILITY_MOVE_DEFAULT_BASES(bar, (foo)(baz));
};

struct baz : bar
{
    UTILITY_MOVE_DEFAULT(baz, (bar), (ptr));

    void* ptr;
};

И выходит конструктор перемещения и оператор присваивания перемещения.

(Кстати, если кто-нибудь знает, как я могу объединить детали в один макрос, это было бы здорово.)

Ответ 4 · рейтинг 4 · Puppy

VS2010 этого не делает, потому что на момент реализации они не были стандартными.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы