Where to use std::move for strings?

ar2015 picture ar2015 · Dec 16, 2017 · Viewed 15.6k times · Source

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)

Answer

Dietmar K&#252;hl picture Dietmar Kühl · Dec 16, 2017

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:

  • When an argument is passed by value a copy can be elided, e.g., when the argument is the result of a different function call. Even when the copy can't be elided, the argument can potentially be made to look like a temporary (e.g., using std::move(arg)).
  • Since a value is passed into the function, i.e., the function already owns a copy, this value can forwarded wherever it is needed.
  • In the worst case the argument gets copied but since a value is needed anyway a copy would need to be created from a 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.