Consider a class with some built-in values and a pointer.
In a move constructor (or a move assignment) I've seen this technique a lot around the web
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
ClassName(ClassName&& move)
{
// stealing resources
this->variable1 = move.variable1;
this->variable2 = move.variable2;
this->pointer = move.pointer;
// resetting move's data
move.variable1 = 0; // really necessary?
move.variable2 = 0; // really necessary?
// this is VERY important: since this temporary is going to be destructed,
// we want to make sure that the temporary's pointer is set to *nullptr*
// Otherwise, the temporary's destructor will free the data we've previously
// moved inside our this->pointer (above)
move.pointer = nullptr;
}
As long as the object has valid state you don't need to reset the values.
If the example instead was about a string class (such as std::string) with the pointer being the pointer to the character data and other two variables being size and capacity, then you would need to reset the variables because otherwise you could end up with an invalid string that wouldn't be safe to use.
If the example instead was about a string class (such as std::string) with the pointer being the pointer to the character data and other two variables being size and capacity, then you would need to reset the variables because otherwise you could end up with an invalid string that wouldn't be safe to use.
For a temporary about to die, what's the sense of resetting it's size variable to 0? I don't see where could the problem arise
The problem arises when you are not dealing with a temporary that is about to die.
This is entirely legal:
1 2 3
std::string a = "abcdefghjiklmnopqrstuvwxyz";
std::string b(std::move(a));
a = "abcdeghijklmopqrst";
But, if the implementer of std::string didn't bother to set it's size variable to 0, it might be assumed that since the size is non-zero and greater than that of the string we're assigning, that no memory needs to be allocated, which would have undesired results.
Keep class invariants intact when moving from an object.
Stroustrup said that, when std::move() is used, one shouldn't rely on that variable's data any longer.
True. But, you should be able to rely on the object's state. In other words, you should be able to assign (either copy or move) if those operations are supported by the object, (and possibly more.)
cire wrote:
Keep class invariants intact when moving from an object.
gedamial wrote:
Tell me more about this
I should have said "prefer to keep class invariants intact when moving from an object," as it's not always possible, and doesn't always make sense to do so. There is plenty of discussion to be found via your favorite search engine.
The standard only requires that:
a. for any type, a moved-from object should be safely destructible.
b. for a Swappable type that uses std::swap, a moved-from object should also be safely move-assignable.
c. for types defined by the standard library, a moved-from object should be in some valid state.
This is what I attempt to do:
a. any moved-from object would be in some valid state.
b. in addition, if the type is default constructible, after move construction, the moved-from object would be in a default constructed state.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
struct str
{
std::size_t sz = 0 ;
char* buffer = nullptr ;
str() noexcept = default ;
// ...
void swap( str& that ) noexcept { using std::swap ; swap(buffer, that.buffer ) ; swap( sz, that.sz ) ; }
str( str&& that ) noexcept { this->swap(that) ; } // 'that' is put into a default-constructed state
// dual-purpose (copy and move) assignment operator
str& operator= ( str that ) noexcept { this->swap(that) ; return *this ; } // 'that' is put into a valid state
// ...
};