Я портирую большой проект с 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 при передаче той же переменной?)
У меня есть два вопроса, основной практический вопрос и дополнительный вопрос любопытства:
Primary: Кодер, который изменил этот код с использования стандартных ссылок на rvalue-ссылки, предположительно сделал так, думая, что переместить семантику были лучшими в этой ситуации. Я могу понять, что, хотя присваивание включает в себя только копирование значений указателя, что не так много работы. Как бы я, если они подходят, правильно использовать rvalue-ссылки для этого кода?
Secondary: (Только любопытство.) Что компиляторы 2007 и 2010 годов сделали из этого кода? Было ли это прочитано как ссылка на ссылку? Сделал два &
операторы объединяются и становятся единой ссылкой? Поскольку это был, по-видимому, неверный синтаксис, но он компилировался и работал нормально, что же он делал?
В качестве решения вашей проблемы вы можете использовать std :: forward.
Просто измените свой код на:
#include <utility>
CopyAndTakeOwnershipFrom(std::forward<VOperandValue>(oOther));
Проблема возникает потому, что вы хотите переместить семантику перемещения через 2 функции, но в первой функции перемещенный объект получает имя. С именем это уже не r-значение без std :: forward, а l-значение, которое нельзя использовать для T&& параметр в CopyAndTakeOwnershipFrom.
То, что это работало раньше в C ++ Builder 2010, похоже, является ошибкой, исправленной в XE4.
Других решений пока нет …