class MyObject
{
public:
MyObject(fs::path& filename) { ... }
private:
...; // [edit] removed poor example with std::vector
};
int main()
{
MyObject myObject("1_gb_file.dat");
// Actually I want something else
myObject = MyObject("2_gb_file.dat"); // <- at one point I have 3gb of file loaded unnecessarily!
}
How can I replace myObject
without ever having both allocated at once?
In some ways I'd like the operator=() to be able to happen first, with something a bit like return value optimization to construct the object in-place. A simple way to do what I want is the following, but it means I have a nullable object. I want myObject
to be guaranteed always valid but I also want to be able to replace it.
int main()
{
std::optional<MyObject> myObject("1_gb_file.dat");
// Better, but not atomic
myObject.reset();
myObject = MyObject("2_gb_file.dat");
// Better, but myObject.reset() still exists and optional holds an extra bool
myObject.emplace("2_gb_file.dat");
}
Assuming a move assignment operator exists,
MyObject myObject("1_gb_file.dat");
// Actually I want something else
{
auto tmp = std::move(myObject);
// destroys myObject
}
myObject = MyObject("2_gb_file.dat");
Shorter version (ty, @o11c):
// destroy myObject
MyObject(std::move(myObject));
16.4.6.15 Moved-from state of library types
Objects of types defined in the C++ standard library may be moved from (11.4.5.3). 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.
3.67 valid but unspecified state
〈library〉 value of an object that is not specified except that the object’s invariants are met and operations on the object behave as specified for its type
Example 1: If an object
x
of typestd::vector<int>
is in a valid but unspecified state,x.empty()
can be called unconditionally, andx.front()
can be called only ifx.empty()
returns false.
Somewhat related, Jon Kalb makes some good points about how being able to call x.empty()
is actually a bad thing for performance, because moving must waste time altering size and capacity. Even worse for std::list using sentinel nodes:
https://www.youtube.com/watch?v=fcRHiFH04a4
Note that without actually invoking MyObject(MyObject&&)
or MyObject& operator=(MyObject&&)
, no move actually happens. For example std::ignore = std::move(myObject)
does nothing. std::move()
is just a cast.