Where to use std::move for strings?
Asked Answered
O

2

22

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)
Oarfish answered 16/12, 2017 at 1:0 Comment(0)
M
14

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.

Morrismorrison answered 16/12, 2017 at 1:37 Comment(0)
G
0

The old value is consumed when moving the string:

  std::string sa = "Was the string";
  std::string sb = std::move(sa);
  std::cout << "Old [" << sa << "] new [" << sb << "]" << std::endl;

Output: Old [] new [Was the string]

I assume, the old value goes into the new string without copying. This should be efficient when you need to assign the string field in some structure to pass as a parameter, like in ROS:

  std::string my_message = method_that_returns_just_a_string();
  std_msgs::String message;
  message.data = std::move(my_message);

my_message will now contain some "valid unspecified value". The example output contains the empty string but you should not rely on this.

It will be no unnecessary cloning of maybe 1Mb of XML.

Gettings answered 12/5, 2021 at 7:40 Comment(2)
The standard says it's in a valid but unspecified state after move. Why do you expect a string using SSO to be empty after a move?Lh
Corrected to say the string is actually "valid unspecified" by the standard.Bloom

© 2022 - 2025 — McMap. All rights reserved.