Std::Ostringstream Printing the Address of the C-String Instead of Its Content

std::ostringstream printing the address of the c-string instead of its content

The expressionstd::ostringstream() creates a temporary, and operator<< which takes const char* as argument is a free function, but this free function cannot be called on a temporary, as the type of the first parameter of the function is std::ostream& which cannot be bound to temporary object.

Having said that, <<std::ostringstream() << "some data" resolves to a call to a member function which is overloaded for void* which prints the address. Note that a member function can be invoked on the temporary.

In order to call the free function, you need to convert temporary (which is rvalue) into a lvalue, and here is one trick that you can do:

 std::cout << "Inline        : "
<< dynamic_cast<std::ostringstream&>(
std::ostringstream().flush() << "some data"
).str()
<< "\n";

That is, std::ostringstream().flush() returns std::ostream& which means, now the free function can called, passing the returned reference as first argument.

Also, you don't need to use dynamic_cast here (which is slow, as it is done at runtime), for the type of the object is pretty much known, and so you can use static_cast (which is fast as it is done at compile-time):

 std::cout << "Inline        : "
<< static_cast<std::ostringstream&>(
std::ostringstream().flush() << "some data"
).str()
<< "\n";

which should work just fine.

sending c-string to non-lvalue std::ostringstream weird behaviour

In C++03, the non-member operator<< (source) which is responsible for printing out C strings, namely

template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,
const char* s );

cannot accept an rvalue stream, so instead a member overload (inherited from the std::ostream base class) is chosen (source):

basic_ostream& operator<<( const void* value );

This prints out the address.

In C++11, there is an rvalue stream insertion operator,

template< class CharT, class Traits, class T >
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os,
const T& value );

which ensures that the behaviour will be the same for lvalue and rvalue streams. Note that this overload could not have been written in C++03, as the only way to bind to an rvalue would be through a const lvalue reference.

The first string argument to a stringstream is saved as a pointer/garbage

The problem is indeed that the stream is a temporary.

Before C++11, there was no non-member operator<< overload that took an rvalue reference as the first parameter (aka, allowing writes to temporary streams).

As such, the only valid operator<< overloads were the members, since all non-member overloads take a non-const reference and as such will not bind to temporaries. One of those non-member overloads is the one responsible for printing C strings (aka char const*). Here's one of the member overloads:

basic_ostream<Ch, Traits>& operator<<(void* p);

And guess what your string literal liberally converts to. :)

After the first string, you get a normal reference back from the call to operator<<, which will then allow the non-member overloads to be viable.

Console couts a memory address instead of string

When people say that strings are like arrays, they mean specifically "c-strings", which are just char arrays (char*, or char []). std::string is a separate C++ class, and is not like an array. See this question for a definition of C-strings.

In your example, str1 is actually an array of std::strings, and when you print it, you're printing the pointer address.

Below is an example using both C++ std::string, and a C-string, to illustrate the difference. In general, when writing C++, you should prefer std::string.

const int n = 2;
std::string str1; //A c++ std::string, which is not like an array
char cstr1[n+1]; // a c-string, which is array-like

for(int i = 0; i < n; ++i) {
char input = '\0';
cout<<"enter letter: ";
cin>>input;
str1.push_back(input); //Append to c++ string
cstr1[i] = input; //Add to array-like c-string
}
cstr1[n] = '\0'; //Ensure C-string is null-terminated

cout << "As C++: " << str1 << std::endl;
cout << "AS C: " << cstr1 << std::endl;
cout << "C++ can convert to C-string: " << str1.c_str() << std::endl;

Added const to n, to make it valid C++, since you shouldn't create arrays from non-const variables

Why does calling cout.operator(const char*) print the address instead of the character string?

When you do cout.operator<<(str) you call cout's operator << member function. If we look at what member functions overloads cout has we have

basic_ostream& operator<<( short value );
basic_ostream& operator<<( unsigned short value );

basic_ostream& operator<<( int value );
basic_ostream& operator<<( unsigned int value );

basic_ostream& operator<<( long value );
basic_ostream& operator<<( unsigned long value );

basic_ostream& operator<<( long long value );
basic_ostream& operator<<( unsigned long long value );

basic_ostream& operator<<( float value );
basic_ostream& operator<<( double value );
basic_ostream& operator<<( long double value );

basic_ostream& operator<<( bool value );

basic_ostream& operator<<( const void* value );

basic_ostream& operator<<( std::nullptr_t );

basic_ostream& operator<<( std::basic_streambuf<CharT, Traits>* sb);

basic_ostream& operator<<(
std::ios_base& (*func)(std::ios_base&) );

basic_ostream& operator<<(
std::basic_ios<CharT,Traits>& (*func)(std::basic_ios<CharT,Traits>&) );

basic_ostream& operator<<(
std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&) );

If you notice, there isn't one for a const char*, but there is one for a const void*. So, your const char* is converted to a const void* and that version of the function prints the address held by the pointer.

What you need to do is call the non member function overload of operator<< and to do that you can use

cout << str;

Printing a string to a temporary stream object in C++

The overload ostream& operator<< (ostream& out, const char*) is not viable because your temporary won't bind to the non-const reference ostream&. There isn't really much you can do about that (since you can't cast an rvalue to an lvalue) other than declaring a local variable and using that:

{
specialstringstream ss;
ss << "Hello world" << std::endl; // OK, can bind to lvalue
}

Possible solution: You could declare another overload that accepts an rvalue reference:

std::ostream & operator<<(specialstringstream && o, const char * s)
{
return o << s; // note: *not* "std::move(o)"
}


Related Topics



Leave a reply



Submit