Страницы

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

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

Можно ли присваивать объекту, который уже был использован в move-семантике?

#cpp #cpp11 #language_lawyer


Предположим, есть код:

struct example  
{  
    example() {  
        ptr = new int(2);    
    }  
    example(example&& rhs) { 
        ptr = rhs.ptr;
        rhs.ptr = nullptr;  
    }  
    ~example()  {
        delete ptr;
    }  
    int a = 2;  
    char b = 'a';  
    int* ptr;  
};

int main()  
{  
    example e;  
    int* a = new int(1);  
    example move_e = std::move(e);  
    e = {1,'b', a};
}


Теперь вопрос: корректно ли после перемещения объекта e опять ему присваивать что-либо?
Я знаю, что обращаться к полям после перемещения будет undefined behavior, но а если
что-то присваивать объекту? Всё вроде законно, и после присваивания его поля снова
будет можно использовать как r-value... 

Прав ли я?
    


Ответы

Ответ 1



Безусловно можно. Есть даже пример в черновике Стандарта: T old_val = std::move(obj); obj = std::forward(new_val); return old_val; Ну и сама выдержка из Стандарта про то, что представляет из себя перемещенный объект: Objects of types defined in the C++ standard library may be moved from ([class.copy]). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state. Т.е. такие объекты находятся в корректном, но неопределенном состоянии. В частности, все функции-члены такого объекта должны отрабатывать корректно, в том числе и оператор =. Стоит обратить особое внимание, что указанная цитата относится к типам стандартной библиотеки. Для пользовательских классов программист сам задает состояние объекта после перемещения, т.к. сам же и пишет реализацию перемещающих функций. Ссылка на ответ на enSO. При этом, чтобы Ваш код был работоспособен, требуется добавить соответствующий конструктор и оператор присваивания (в данном случае подойдет перемещающий): example(int a, char b, int* ptr) : a(a), b(b), ptr(ptr) {} example& operator=(example&& o) { a = o.a; b = o.b; ptr = o.ptr; o.ptr = nullptr; return *this; }

Ответ 2



Я знаю, что обращаться к полям после перемещения будет undefined behavior Весьма сомнительное утверждение. Конструктор перемещения строится таким образом, чтобы последующий вызов деструктора (который, несомненно, обращается к полям) на объекте отработал корректно. Было бы весьма странно, если бы обращение к полям не из деструктора рассматривалось как UB (хотя стандарт я не смотрел). С другой стороны, объект обеспечит съедобное для деструктора состояние, но он не обязан обеспечивать цельное с точки зрения бизнес-логики состояние. Т. е. поле, которое при других условиях не может быть равно null, вполне может таковым оказаться. Об этом надо думать. Автор класса знает, как он его использует, а всем остальным стоит рассматривать такой объект просто как мусор. а если что-то присваивать объекту? Не вижу причин, почему так было бы нельзя сделать. Хотя, если разработчик класса очень постарается, то и на этом можно что-то плохое словить.

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

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