I was reading this post.
And I reached to the following code.
I was wondering:
Is std::move
useful for strings (assuming the string is long enough)?
Does it invalidate the previous string?
Where should I use it and where I should not?
.
class Name
{
public:
Name(std::string firstName, std::string lastName)
: firstName_(std::move(firstName))
, lastName_(std::move(lastName)) {}
void print() const
{
std::cout << lastName_ << ", " << firstName_ << '\n';
}
private:
std::string firstName_;
std::string lastName_;
};
My technique was always using
constructor(const std::string& argument): field(argument)
The idiom of accepting parameters by value makes sense when the value of a movable type is consumed. With consuming a value I mean that the value is forward to something which requires its own copy of the value. The reason here is this:
std::move(arg)
).T const&
parameter, too, and the additional move operation is assumed to be comparatively cheap.Thus, the expectation is that in the worst case there may be small additional work but in the normal case there is substantially less work as only one move operation instead of a copy is done.
For std::string
the argument is slightly harder than for other movable types due to its quite common short string optimization: instead of a few pointer operations there may be a need to transfer bytes. However, in practice copying the short string or the pointers is effectively just a memcpy()
potentially followed by an operation indicating that the source of the move operation no longer contains a string which needs be released.
Thus, the simple rule is
When consuming a movable object accept the argument by value and move the object rather than pass the argument via a
T const&
and create a copy to consume the result.