13

Consider:

std::string        s_a, s_b;

std::stringstream  ss_1, ss_2;

// at this stage:
//     ss_1 and ss_2 have been used and are now in some strange state
//     s_a and s_b contain non-white space words

ss_1.str( std::string() );
ss_1.clear();

ss_1 << s_a;
ss_1 << s_b;

// ss_1.str().c_str() is now the concatenation of s_a and s_b, 
//                    <strike>with</strike> without space between them

ss_2.str( s_a );
ss_2.clear();

// ss_2.str().c_str() is now s_a

ss_2 << s_b;  // line ***

// ss_2.str().c_str() the value of s_a is over-written by s_b 
// 
// Replacing line *** above with "ss_2 << ss_2.str() << " " << s_b;"
//                    results in ss_2 having the same content as ss_1.

Questions:

  1. What is the difference between stringstream.str( a_value ); and stringstream << a_value; and, specifically, why does the first not allow concatenation via << but the second does?

  2. Why did ss_1 automatically get white-space between s_a and s_b, but do we need to explicitly add white space in the line that could replace line ***: ss_2 << ss_2.str() << " " << s_b;?

user1823664
  • 1,071
  • 9
  • 16

2 Answers2

5

The problem you're experiencing is because std::stringstream is constructed by default with ios_base::openmode mode = ios_base::in|ios_base::out which is a non-appending mode.

You're interested in the output mode here (ie: ios_base::openmode mode = ios_base::out)

std::basic_stringbuf::str(const std::basic_string<CharT, Traits, Allocator>& s) operates in two different ways, depending on the openmode:

  1. mode & ios_base::ate == false: (ie: non-appending output streams):

    str will set pptr() == pbase(), so that subsequent output will overwrite the characters copied from s

  2. mode & ios_base::ate == true: (ie: appending output streams):

    str will set pptr() == pbase() + s.size(), so that subsequent output will be appended to the last character copied from s

(Note that this appending mode is new since c++11)

More details can be found here.

If you want the appending behaviour, create your stringstream with ios_base::ate:

std::stringstream ss(std::ios_base::out | std::ios_base::ate)

Simple example app here:

#include <iostream>
#include <sstream>

void non_appending()
{
    std::stringstream ss;
    std::string s = "hello world";

    ss.str(s);
    std::cout << ss.str() << std::endl;

    ss << "how are you?";
    std::cout << ss.str() << std::endl;
}

void appending()
{
    std::stringstream ss(std::ios_base::out | std::ios_base::ate);
    std::string s = "hello world";

    ss.str(s);
    std::cout << ss.str() << std::endl;

    ss << "how are you?";
    std::cout << ss.str() << std::endl;
}

int main()
{
    non_appending();
    appending();

    exit(0);
}

This will output in the 2 different ways as explained above:

hello world
how are you?
hello world
hello worldhow are you?
Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213
  • 2
    Another way to do this, without modifying the `openmode`, is to seek to the end of the stream before insertion. `ss.str(s); ss.seekp(0, std::ios_base::end); ss << "how are you?";` – Praetorian Nov 26 '12 at 03:57
  • Thanks; minor tweak: In non_appending(), if the first initialization is make longer, say, std::string s = "A very long hello world!"; then the second line of output would be: "how are you?hello world!" – user1823664 Nov 26 '12 at 04:06
  • 1
    @user1823664 - to understand what is happening here you need to understand that the `stringstream's` `streambuffer` uses internal pointers to store positions in the buffer. `str()` uses these pointers to create a new `std::string` - and depending on where `egptr` or `pptr` are you'll get a string built from these. – Steve Lorimer Nov 26 '12 at 04:15
4

Suggest you read stringstream reference: http://en.cppreference.com/w/cpp/io/basic_stringstream

std::stringstream::str() Replaces the contents of the underlying string

operator<< Inserts data into the stream.

Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179
billz
  • 44,644
  • 9
  • 83
  • 100
  • Still doesnt explain why `str()` followed by `<<` has such behavior, unless I am misreading something – Karthik T Nov 26 '12 at 03:38
  • 2
    The other thing is that after using `str()`, ththings get out of sync. For example, using `str()` on a new instance, followed by `operator<<` will insert the second part at the beginning, overwriting the first part. http://ideone.com/OPR9Ps – chris Nov 26 '12 at 03:47
  • 1
    He's asking why calling `operator<<` **after** using `str` to replace the contents doesn't **append** to the end - and the answer is because `openmode` doesn't have `ate` set – Steve Lorimer Nov 26 '12 at 03:56