Страницы

Поиск по вопросам

воскресенье, 29 декабря 2019 г.

Что происходит с объектом после std::move?

#cpp #cpp11 #move


Пример класса: 

class Move
{
   public:
   Move(std::string name) : name(name) {};
   Move(Move&& move) : name(std::move(move.name)) {};
   void Set(std::string&& add)
   {
      this->name.append(add);
   }
   void Print()
   {
      std::cout << this->name << std::endl;
   }

private:
   std::string name;
};


После выполнения данного кода: 

Move move0("string");
Move move1 = std::move(move0);


объект move0 будет иметь вид {name=""}, но "" пустая std::string все равно занимает
место в памяти и не освобождает ее сама по себе. Получается, что перемещение более
эффективное в некоторых случаях, но при этом старые ссылки остаются "доживать" свой
век? Не понятно, что происходит с объектами после использования на них std::move, хотя
казалось бы, что они должны становиться nullptr. 

UPD

Почему есть ситуации  в которых после std::move объект из которого было произведено
перемещение, остается без изменений? 

int main()
{
   std::string str = " Another";
   m.Set(std::move(str));
   return 0;
}


После std::move(str), сам str остался без изменений. 
    


Ответы

Ответ 1



С++ - это вам не Java. В нем объекты не могут быть null, и не являются ссылками. что происходит с объектами после использования на них std::move Строго говоря, move ничего не делает, кроме как превращает выражение в rvalue ("маскирует" под временную переменную). Это влияет в первую очередь на разрешение перегрузок, и в том числе заставляет срабатывать перемещающие конструктор и оператор присваивания, вместо копирующих. Изменения в объекте вызываются именно перемещающими конструктором или оператором присваивания, куда он передается как аргумент (если это вообще происходит). Что конкретно происходит с объектом после перемещения из него - зависит того, как написаны перемещающие конструктор и оператор присваивания. Про стандартные контейнеры, например, говорят что они "are left in valid, but unspecified state". В большинстве реализаций они становятся пустыми, но это не гарантируется. казалось бы, что они должны становиться nullptr. Объекты нельзя "сделать nullptr", но можно переформулировать вопрос: Почему перемещение из объекта не уничтожает его, и требуется дополнительный вызов на нем деструктора? Я так понимаю, потому что это очень усложнило бы работу компилятора. Например, для void foo(std::string s) { // ... } деструктор всегда вызывается для s при возврате из функции. А если перемещение из объекта уничтожало бы его, то компилятору потребовалось бы отслеживать, какие переменные перемещаются, а какие нет. Если переменные перемещаются при определенном условии, то пришлось бы запоминать для каждого объекта, был он перемещен или нет, и в зависимости от этого вызывать или не вызывать на нем деструктор при выходе из функции. Это выглядит сложно.

Ответ 2



В языке С++ нет никакой встроенной семантики перемещения. В языке С++ есть только rvalue-ссылки, категории результатов выражений, и связанные с ними правила overload resolution. А что вы сами "слепите" из этих базовых свойств языка - семантику перемещения или что-то другое - определяется только вашим кодом (или, соответственно, библиотечным кодом). Вы передали переменную str в метод Set по rvalue-ссылке, ни внутри метода Set вы только копируете полученный аргумент add и никакого физического перемещения данных из него даже и не пытаетесь делать. Если вы "надеетесь" получить физическое перемещение из add внутри метода Set, то это ваша задача - разрешить методу std::string::append переместить данные из своего аргумента void Set(std::string&& add) { this->name.append(std::move(add)); } Но и в этом случае фактическое перемещение данных из add произойдет только в том случае, если сам метод std::string::append примет решение выполнить перемещение. И здесь я вам могу сказать по секрету, что метод std::string::append в принципе никогда ничего не перемещает. В большинстве применений std::string::append это просто-напросто невозможно. Оптимизировать std::string::append через физическое перемещение возможно только в том случае, когда строка-получатель изначально пуста. А это слишком частная ситуация, чтобы ее оптимизировать. (Я бы лично, возможно, это сделал, но разработчики класса std::string рассудили по-другому.) И постарайтесь уяснить, что в С++ обычное копирование - это тоже один из вариантов перемещения. Поэтому рассуждения вроде "объект остался неизменным, значит перемещения не произошло" - бессмысленны.

Комментариев нет:

Отправить комментарий