views:

86

answers:

3

The following segment demonstrates my issue: (compilation error on GCC)

stringstream ss;
string s;
ss << "Hello";

// This fails:
// s.swap(ss.str());

// This works:
ss.str().swap(s);

My error:

constSwap.cc:14: error: no matching function for call to 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >::swap(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
basic_string.tcc:496: note: candidates are: void std::basic_string<_CharT, _Traits, _Alloc>::swap(std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]

While I understand that str() in stringstream returns a temporary, it does not make sense and was not immediately apparent that I should have been calling the swap on the temporary with the local variable as parameter instead of my first instinct.

Obviously straight assignment works better, and newer C++ standards have move semantics which is perfect, but these are not available for my implementation.

Visual Studio does not give this problem due to it being relaxed about the C++ standard. I kinda already understand the whole const reference to a temporary thing (which I assume is the reason for my compilation errors).

My question: Can anyone explain to me if this is the only solution, and perhaps explain to me the how to think about this in future so I can spot and work around similar issues?

(If no-one has any great insights I'm at least posting this here for people with similar issues)

+6  A: 

You can't bind a temporary to a non-const reference. For this reason the temporary returned from ss.str() can't be passed to std::string::swap which expects to modify its parameter (therefor it takes its argument using non-const&).

The second version works as you're calling a member function on the temporary object which is permitted.

But why do you want to swap in the first place? Normally, a simple:

std::string s(ss.str());

should be good enough. This isn't less efficient than the swap (at least in C++0x with move semantics), but at the same time a lot more readable.

hkaiser
+1 for typing faster than me.
Cogwheel - Matthew Orlando
Ken Bloom
You're certainly right, I edited my answer accordingly.
hkaiser
+1  A: 

After having used the swap-with-temporary idiom enough times, with lines like

std::vector<int>().swap(v); // clear and minimize capacity

or

std::vector<int>(v).swap(v); // shrink to fit

this does not seem so out of place. It's normal to call swap as a member function of a temporary object. Of course, it's not so idiomatic to use swap to fill in a default-constructed string instead of using a copy constructor, as already mentioned.

Cubbi
This best addressed my question. I guess I should just get used to the idiom.
Marius
A: 

The reason that you cannot pass the temporary as argument to swap is that the argument is passed by non-const reference. And temporaries can only be bound by const-references. This is spread over §8.5.3, with the appropriate being in paragraph 5, second bullet:

§8.5.3 A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

  • [bullet one, does not apply here: binding to non-const ref ]

  • Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const).

The reason why writing the call in the opposite direction works is that the standard allows to call mutating member functions on temporary objects.

§3.10/10 An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [Example: a member function called for an object (9.3) can modify the object. ]

The line of reasoning that you request for the future is that, while you can modify a temporary through it's own functions, you cannot pass it to a function or method that could modify it (pass by non-const reference)

David Rodríguez - dribeas