Неправильное использование rvalue ссылок из старого кода: как мне обновить его до современного, правильного использования?

Я портирую большой проект с C ++ Builder 2010 на XE4, и только что натолкнулся на этот код. Он скомпилировал, запустил и, по-видимому, работал в CB2010, и в соответствии с журналом изменений был добавлен, когда мы еще использовали C ++ Builder 2007. Ни один из этих компиляторов не поддерживает ссылки на rvalue.

VOperandValue& VOperandValue::operator=(VOperandValue&& oOther) {
// Assignment operator takes full control of all pointers etc, and sets oOther
// to not own anything
if (static_cast<void*>(this) != static_cast<void*>(&oOther)) {
CopyAndTakeOwnershipFrom(oOther);
}
return *this;
}

void VOperandValue::CopyAndTakeOwnershipFrom(VOperandValue&& oOther) {
// Lots of assignments to self's members, and clearing fields of oOther
}

Следует отметить две вещи:

  • operator= изменение скопированного объекта является преднамеренным. Этот класс объединяет ресурсы, которые могут принадлежать только одному объекту за раз. Это выходит за рамки этого вопроса, но поведение, хотя и странное, такое, как задумано. Существует также конструктор копирования и несколько других методов, которые были изменены аналогичным образом. Методы предназначены для использования в тех случаях, когда скопированные объекты либо станут недействительными (например, уничтожены), либо будут использованы повторно для хранения чего-то нового. Я не имею понятия почему static_cast<void*> необходим для сравнения указателей на объекты одного типа; мне кажется, это очень странная вещь.
  • Код был изменен с использованием стандартных ссылок (т.е. VOperandValue& oOther) к rvalue-ссылкам. Несмотря на то, что поддержка ссылок на rvalue добавляется только в C ++ Builder XE, версии после C ++ Builder 2010, компилятор с радостью принял и скомпилировал ее, и код, похоже, сработал при запуске.

Теперь этот код загружен в XE4, он не может скомпилировать в строке CopyAndTakeOwnershipFrom(oOther) со следующими ошибками:

[bcc32 Ошибка] OperandValue.cpp (178): E2559 Не удается инициализировать rvalue
ссылка типа VOperandValue с lvalue типа VOperandValue

[bcc32 Ошибка] OperandValue.cpp (178): E2342 Несоответствие типов в параметре
‘oOther’ (хотел ‘VOperandValue &&’, получил’ VOperandValue ‘)

(Я не понимаю этих ошибок, так как строка 178 является строкой CopyAndTakeOwnershipFrom(oOther);, а также oOther похоже, определенно был определен в списке параметров метода как rvalue-ссылка. Почему тогда возникает проблема с lvalue non-r-ref при передаче той же переменной?)

У меня есть два вопроса, основной практический вопрос и дополнительный вопрос любопытства:

  1. Primary: Кодер, который изменил этот код с использования стандартных ссылок на rvalue-ссылки, предположительно сделал так, думая, что переместить семантику были лучшими в этой ситуации. Я могу понять, что, хотя присваивание включает в себя только копирование значений указателя, что не так много работы. Как бы я, если они подходят, правильно использовать rvalue-ссылки для этого кода?

  2. Secondary: (Только любопытство.) Что компиляторы 2007 и 2010 годов сделали из этого кода? Было ли это прочитано как ссылка на ссылку? Сделал два & операторы объединяются и становятся единой ссылкой? Поскольку это был, по-видимому, неверный синтаксис, но он компилировался и работал нормально, что же он делал?

1

Решение

В качестве решения вашей проблемы вы можете использовать std :: forward.

Просто измените свой код на:

#include <utility>
CopyAndTakeOwnershipFrom(std::forward<VOperandValue>(oOther));

Проблема возникает потому, что вы хотите переместить семантику перемещения через 2 функции, но в первой функции перемещенный объект получает имя. С именем это уже не r-значение без std :: forward, а l-значение, которое нельзя использовать для T&& параметр в CopyAndTakeOwnershipFrom.
То, что это работало раньше в C ++ Builder 2010, похоже, является ошибкой, исправленной в XE4.

0

Другие решения

Других решений пока нет …