Я хочу разобраться в следующей ситуации.
Допустим, имеется класс 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. Во многих книжках при описании семантики перемещения применяется странная фраза: после перемещения объект имеет корректное, но неопределенное состояние. Но нигде не говорится, что с этим делать.
Все гарантии, касательно самописного класса, устанавливаются программистом, этот класс написавшим. Т.е. Вы сами определяете, в каком состоянии будет находится объект после перемещения, стандарт С++ не налагает на программиста никаких ограничений. Все объекты стандартной библиотеки подчиняются правилу, описанному в [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() и прочие — неизвестно.
Это довольно неплохой ориентир для написания собственных классов, т.е. после перемещения, объекты Ваших классов можно продолжать использовать. На практике же, минимальным и достаточным требованием для объекта после перемещения является его корректное удаление деструктором. Т.е. при завершении времени жизни объекта, не должно произойти никаких неприятностей. В остальном же — никаких гарантий, т.к. использовать объект после того, как он был перемещён семантически некорректно.
Сборка персонального компьютера от Artline: умный выбор для современных пользователей