Benji Mizrahi
Benji Mizrahi

Reputation: 2164

Concatenating two moved strings

The code below:

#include <iostream>
#include <string>
using namespace std;

int main() {
    string s1 = "hello";
    string s2 = "my";
    string s3 = "world";
    string s4;
    s4 = move(s1) + move(s2) + move(s3);
    cout << "s4(" << s4 << ") = s1(" << s1 << ") + s2(" << s2 << ") + s3(" << s3 << ")"<< endl;
}

gives the following output:

s4(hellomyworld) = s1() + s2(my) + s3(world)

Can you explain what is going on? (Tested with XCode 4.6.1)

EDIT: I expected to see: s4(hellomyworld) = s1() + s2() + s3()

Upvotes: 1

Views: 116

Answers (3)

jmihalicza
jmihalicza

Reputation: 2089

s4 = move(s1) + move(s2) + move(s3) is in fact operator=(s4, operator+(operator+(move(s1), move(s2)), move(s3)); The implementation of operator+ is not required to modify its rvalue argument, but is allowed. Presumably the implementation is something like this:

string operator+(string&& left, string&& right)
{
   string result(left);
   return result += right;
}

where right is only read.

Upvotes: 1

Benji Mizrahi
Benji Mizrahi

Reputation: 2164

template<class _CharT, class _Traits, class _Allocator>
_LIBCPP_INLINE_VISIBILITY inline
basic_string<_CharT, _Traits, _Allocator>
operator+(basic_string<_CharT, _Traits, _Allocator>&& __lhs, basic_string<_CharT, _Traits,  _Allocator>&& __rhs)
{
    return _VSTD::move(__lhs.append(__rhs));
}

So seems like during the concatenation __rhss stayed unchanged. After all the string is accumulated at s1 the move assignment of s4 emptied s1.

Upvotes: 0

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385295

I assume you expected s4(hellomyworld) = s1() + s2() + s3().

First of all, movement leaves the source object in "a valid state with an unspecified value" ([C++11: 21.4.2/2]) — that is, you can't make any claims about what a string's value will be after you moved from it.

Secondly, std::move is a misnomer, in that it doesn't actually move anything. Movement is implemented by swapping things around, and there is no guarantee that your string operator+ does anything of the sort (see jmihalicza's answer for a sample implementation); all std::move does is obtain an rvalue reference that may be moved from.

[C++11: 21.4.6.3/1] says about std::basic_string& assign(basic_string&& str) noexcept (which is the function you're really invoking, when you follow all the breadcrumbs from operator+) that "a valid implementation is swap(str)", but that doesn't mean a swap has to occur.

Upvotes: 4

Related Questions