c++ перемещение и неизменяемый класс

225
27 февраля 2019, 10:10

Я хочу разобраться в следующей ситуации.

Допустим, имеется класс A, объекты которого после конструирования не могут изменяться, предоставляя лишь геттеры для получения доступа к своим данным. То есть, валидность создаваемого объекта класса A контролируется в конструкторе. И на этот факт опирается очень многое, например, работа других классов, которые пользуются объектами класса A.

Проблема в том, что классу A нужен конструктор перемещения и перемещающий оператор присваивания. После перемещения объекта класса A изначальный объект становится невалидным:

A a_1{ 1, 2, 3 };
A a_2 = move(a_1);
do_something(a_1);// Беда...

И я не совсем понимаю, что делать в этой ситуации...

С объектами изменяемых классов таких проблем, как правило, нет. Например, объект std::vector после перемещения становится пуст и может использоваться дальше:

vector<float> vf_1 { 1.f, 2.f };
vector<float> vf_2 = move(vf_1);
vf_1.push_back(3.f);// OK

Мои вопросы:

1) Является ли эта особенность с перемещением объекта неизменяемого класса нормой?

2) Что с этим делать? Ставить пометки большими красными буквами о том, что объект такого неизменяемого класса после перемещения становится невалиден, и либо не должен использоваться, либо должен вновь вызывать конструктор, чтобы сформировать валидное состояние?

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

PS. Во многих книжках при описании семантики перемещения применяется странная фраза: после перемещения объект имеет корректное, но неопределенное состояние. Но нигде не говорится, что с этим делать.

Answer 1

Все гарантии, касательно самописного класса, устанавливаются программистом, этот класс написавшим. Т.е. Вы сами определяете, в каком состоянии будет находится объект после перемещения, стандарт С++ не налагает на программиста никаких ограничений. Все объекты стандартной библиотеки подчиняются правилу, описанному в [lib.types.movedfrom]:

Objects of types defined in the C++ standard library may be moved from (15.8). 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.

Что это значит? Это значит, что после того, как объект класса стандартной библиотеки был перемещён, с ним можно продолжать работать. Но его внутреннее состояние неизвестно, т.е. что вернут запросы типа size(), capacity() и прочие — неизвестно.

Это довольно неплохой ориентир для написания собственных классов, т.е. после перемещения, объекты Ваших классов можно продолжать использовать. На практике же, минимальным и достаточным требованием для объекта после перемещения является его корректное удаление деструктором. Т.е. при завершении времени жизни объекта, не должно произойти никаких неприятностей. В остальном же — никаких гарантий, т.к. использовать объект после того, как он был перемещён семантически некорректно.

READ ALSO
std::vector и emplace_back()

std::vector и emplace_back()

Правильно ли я понимаю, что метод std::vector::emplace_back() вызывает конструктор, но не конструктор перемещения?

201
Как в C++ найти наибольший делитель числа?

Как в C++ найти наибольший делитель числа?

Пользователь вводит число N, нужно найти наибольший делитель числа N, не равный N

356
Использование UNICODE в С++ [закрыт]

Использование UNICODE в С++ [закрыт]

Нужно в строке UNICODE написать все символы в обратном порядкеОчень маленький опыт работы с UNICODE по-этому не понимаю что нужно исправить

203