Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ Clear stringstream using one-liner m.swap(std::stringstream());?

I have been using the following code to clear std::stringstream:

m.swap(std::stringstream());

Code probably taken from this SO thread. Recently I compiled my code in Visual Studio 2013 and I received the following warning:

warning C4239: nonstandard extension used : 'argument' : conversion from 'std::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>' to 'std::basic_stringstream<char,std::char_traits<char>,std::allocator<char>> &'
1>          A non-const reference may only be bound to an lvalue

I did not receive any warning in earlier versions of Visual Studio using /W4. I recall I had trouble reliably clearing stringstreams in earlier versions which is why I ended up using that code in the first place.

So is it portable to clear a stringstream that way, and if not could you explain why not? Also is there a portable way to clear a stringstream? Thanks

Edit: Here is the code that was requested. Compile it using /W4 to see the warning.

int _tmain(int argc, _TCHAR* argv[])
{
    std::stringstream m;
    m.swap(std::stringstream());

    return 0;
}
like image 728
user2672807 Avatar asked May 17 '14 21:05

user2672807


3 Answers

Here's the signature of std::stringstream::swap():

void swap( basic_stringstream& other );

As you can see, swap() takes an lvalue-reference to its argument. An rvalue or temporary cannot bind to an lvalue-reference as your compiler is correctly telling you. Some compilers support non-standard extensions that allow you to do this, but I would advise that you stay within the bounds of the standard, it's much safer.

However, while you may not be able to bind the temporary to the lvalue-reference, you can transform the temporary into an lvalue using this std::skipws trick:

m.swap(static_cast<std::stringstream&>(stringstream{} >> std::skipws));

or even

std::stringstream{}.swap(m):

In the first example, the function operator>>() returns a reference to the stream, which is safe to use with swap() because the reference that holds the argument is destroyed at the end of the function.

like image 69
David G Avatar answered Nov 20 '22 01:11

David G


The std::swap function takes two non-const parameters to swap. This is because both parameters are changed when the function is called. Similarly, the std::stringstream::swap(...) function, takes a single non-const parameter to swap with.

For example:

std::vector<int> vec1, vec2;
std::swap(vec1, vec2);

In the above sample, both vec1 and vec2 are altered. When you try to do this...

m.swap(std::stringstream());

... you're trying to assign the contents of the temporary std::stringstream() with the contents of m, which to a compiler looks like a bug, even if your intention was simply to clear the contents of m.

To remove the warning, simply declare a temporary std::stringstream before performing the swap. Like this:

std::stringstream temp;
m.swap(temp);
like image 22
Karl Nicoll Avatar answered Nov 20 '22 03:11

Karl Nicoll


That use of swap is not allowed by the Standard. The declaration is

template <typename CharT, typename Traits, typename Allocator>
void std::basic_stringstream<CharT, Traits, Allocator>::swap(
    basic_stringstream& other);

which implies that both stringstream objects will be modified. But the expression std::stringstream() is an rvalue (specifically, a temporary), and C++ does not allow binding non-const lvalue references to temporaries, since modifying a temporary is not often the real intent.

The easy solution is:

m = std::stringstream();

But if you need to support compilers without C++11 support, neither will work, since in the C++03 Standard, stringstream has no swap function and no public operator=. In that case, you would need to go with:

m.clear();
m.str(std::string());
like image 1
aschepler Avatar answered Nov 20 '22 03:11

aschepler