Как удалить оператор присваивания перемещения и сохранить совместимость со стандартными контейнерами?

У меня есть простая оболочка RAII для управления определенным ресурсом. Вот интерфейс:

struct ResourceWrapper
{
    explicit ResourceWrapper(RESOURCE resource);
    ResourceWrapper(const ResourceWrapper& other);
    ResourceWrapper& operator=(const ResourceWrapper& other);
    ~ResourceWrapper();

    ResourceWrapper(ResourceWrapper&& other) = delete;
    ResourceWrapper& operator=(ResourceWrapper&& other) = delete;
};

Проблема здесь в том, что я больше не могу использовать этот класс с std контейнерами и алгоритмами, как только я явно удалю оператор присваивания перемещения. И, очевидно, мне нужно либо удалить, либо правильно реализовать его, поскольку я только что научился на собственном горьком опыте.

Другой альтернативой может быть реализация присваивания перемещения с помощью обычного оператора присваивания, но я не уверен, как это сделать правильно. Полагаю, мне нужно что-то вроде std::remove_reference? Интересно, удалит ли он слишком много ссылок и приведет к созданию ненужного временного объекта.


person Violet Giraffe    schedule 18.01.2016    source источник
comment
вы препятствуете перемещению, но поощряете копирование? звучит странно. что это за ресурс?   -  person David Haim    schedule 18.01.2016
comment
@DavidHaim: да, точно. Неважно, какой это ресурс, я просто хочу обойтись как можно меньшим количеством кода. Я не могу отказаться от копирования, но я могу отказаться от перемещения.   -  person Violet Giraffe    schedule 18.01.2016
comment
Способ отбрасывания перемещения состоит в том, чтобы не определять его, то, что вы сделали, - это не перемещение перемещения, а перемещение яда, поэтому копирование rvalue невозможно. Это не то же самое.   -  person Jonathan Wakely    schedule 18.01.2016


Ответы (1)


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

Нет, не знаешь.

В вашем классе есть определяемый пользователем конструктор копирования, оператор присваивания копии и деструктор, поэтому компилятор не определяет для вас оператор присваивания перемещения.

Так что просто перестаньте пытаться удалить его, и класс будет скопирован, а не перемещен.

При удаленных операциях перемещения нельзя копировать rvalue типа, т.е. его становится крайне сложно использовать в качестве типа значения (в том числе в контейнерах). С операциями перемещения no он просто сделает глубокую копию rvalue, что безопасно и, возможно, то, что вам нужно.

Удаление операций перемещения имеет смысл только в том случае, если вы хотите, чтобы класс был полностью неперемещаемым и некопируемым, например. что-то вроде типа мьютекса, где важна идентификация объекта, а не его значение. Никогда не имеет смысла иметь копируемый тип с удаленными ходами.

person Jonathan Wakely    schedule 18.01.2016
comment
Я сильно подозреваю, что этого не происходит, и ошибка, над которой я работаю, вызвана назначением перемещения по умолчанию. Думаю, мне придется правильно реализовать назначение движения, чтобы проверить эту гипотезу? - person Violet Giraffe; 18.01.2016
comment
@VioletGiraffe Чтобы проверить такую ​​гипотезу (которая укажет на ошибку в используемом вами компиляторе), вы можете просто реализовать перемещения с точки зрения копий: ResourceWrapper(ResourceWrapper&& other) : ResourceWrapper(other) {}. Обратите внимание, что последний действительно вызовет copy ctor. - person Angew is no longer proud of SO; 18.01.2016
comment
@Angew: конструктор - это не проблема, это задание. Разве ResourceWrapper& operator=(ResourceWrapper&& other) {return *this = other;} не рекурсия? Или это будет только рекурсия, если я использую return *this = std::forward(other)? - person Violet Giraffe; 18.01.2016
comment
@VioletGiraffe, почему бы тебе не попробовать? *this = other - это присвоение из lvalue, поэтому рекурсии нет. - person Jonathan Wakely; 18.01.2016
comment
@VioletGiraffe Это не рекурсия, она будет правильно делегирована. Это будет рекурсия, если вы сделаете *this = std::move(other) - person Angew is no longer proud of SO; 18.01.2016
comment
@Angew: Понятно. Не был уверен, нужен ли мне std::remove_reference или нет. - person Violet Giraffe; 18.01.2016
comment
@VioletGiraffe, какой компилятор вы используете? Если ему не более 4 лет, я был бы очень удивлен, если бы он неправильно определял для вас оператор присваивания перемещения, когда у вас есть объявленные пользователем операции копирования + dtor. - person Jonathan Wakely; 18.01.2016
comment
@JonathanWakely: GCC 4.9. что-то из Android NDK. Совсем не старый. - person Violet Giraffe; 18.01.2016
comment
Я сделал то, что предложил @Angew, и моя ошибка не исчезла. Так что вы оба должны быть правы - проблема не в неявном операторе перемещения. - person Violet Giraffe; 18.01.2016
comment
GCC 4.9 сделает все правильно, так что ваша проблема определенно в другом. Я предполагаю, что вы неправильно определили назначение копии. - person Jonathan Wakely; 18.01.2016