7

This works:

stringstream temp;
temp << i;
result_stream << transform(temp.str());

(transform is a function that takes a string and returns a string; i is an int). However, my attempt to let C++11 create a temporary object without a name didn't work:

result_stream << transform((stringstream() << i).str());

I thought it would work, since the second << should just return the first argument and I'd be able to use str() on that. But I get this error:

error: 'class std::basic_ostream<char>' has no member named 'str'

I'm using g++ 4.8.1 (MinGW-W64).

Is there a way to accomplish this (i.e. write code like this using an unnamed temporary)? (The above code is a bit simplified, and the actual code involves using << on arguments other than int.)

ajb
  • 31,309
  • 3
  • 58
  • 84

4 Answers4

10

This doesn't work because the second << is std::ostream &operator<<(std::ostream &, int); and so the return type is ostream& which has no member str().

You would have to write:

result_stream << transform( static_cast<stringstream &>(stringstream() << i).str() );

Update (2019): According to LWG 1203 the standard may be changed in future (and one major implementation already has) so that this code no longer works, and a simpler code works instead. See this question for detail.

In the interim period, apparently the following works on both old and new:

result_stream << transform( static_cast<stringstream &>(stringstream().flush() << i).str() );
//                                                                    ^^^^^^^^

This should not be a performance penalty since flushing an empty stream has no effect...

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Thanks, that works. But it's starting to look a little hairy, and it doesn't work when `i` is replaced by an expression of my own class, for which I had defined a friend `operator<<`. So I probably won't pursue this any further. – ajb Dec 10 '14 at 00:22
  • Notice: latest clang++ compiler (9.0.0) complains about non-const lvalue reference so to cast we need `static_cast(stringstream() << i).str()` – Krzysztof Dec 13 '19 at 10:37
  • @Krzysztof [works fine in 9.0.0](https://gcc.godbolt.org/z/VenbjY), maybe your code was different – M.M Dec 13 '19 at 12:21
  • @M.M Sorry, I have not mentioned that I was using MacOS. I have copy-pasted your code and compiler gives me error `x.cc:12:24: error: non-const lvalue reference to type 'basic_stringstream<...>' cannot bind to a temporary of type 'basic_stringstream<...>' cout << transform( static_cast(stringstream() << i).str() );` With g++ 9.2.0 it compiles without any problem. – Krzysztof Dec 13 '19 at 13:02
  • @Krzysztof sounds like a compiler bug, I would suggest posting a new question that includes the reproducible example, in case we are both overlooking something new in the standard . (you can refer to this answer in your question if you like) – M.M Dec 13 '19 at 14:00
  • 1
    @M.M I have created new [question](https://stackoverflow.com/questions/59325270/error-when-casting-temporary-object-to-non-const-reference) about that problem. – Krzysztof Dec 13 '19 at 15:15
  • @Krzysztof the LWG report suggests adding a `.flush()` to make the code work on both stdlib implementations, i.e. `transform( static_cast(stringstream().flush() << i).str() )` -- does that work for you? If so then I will add it to my answer – M.M Dec 13 '19 at 21:45
6

The result of the << operator on the temporary stringstream is an ostream. There is no str() method on an ostream.

Use to_string instead:

using std::to_string;

result_stream << transform(to_string(i));

You can define a helper to_string to handle objects not covered by std::to_string.

template <typename T>
std::string to_string (const T &t) {
   std::ostringstream oss;
   oss << t;
   return oss.str();
}

For example, if you had a class Foo that understood redirection to an ostream, and f was an instance of Foo, then you could do:

result_stream << transform(to_string(f));

Try it online!

If you actually want to use a lot of redirection to build up a string before transforming, you could create a helper object for that as well.

struct to_string_stream {
    std::ostringstream oss;
    template <typename T>
    auto & operator << (const T &t) { oss << t; return *this; }
    operator std::string () const { return oss.str(); }
    void clear () { oss.string(std::string()); }
};

Then, you could do something like:

to_string_stream tss;
result_stream << transform(tss << i << ':' << f);

Try it online!

jxh
  • 69,070
  • 8
  • 110
  • 193
6

operator<<() returns a reference to the base class std::ostream contained within the std::stringstream. The base class doesn't contain the str() method. You can cast it back down to a std::stringstream&:

result_stream << transform(static_cast<std::stringstream&>(std::stringstream() << i).str()); 
David G
  • 94,763
  • 41
  • 167
  • 253
5

Tried and failed to do this for C++11 (in 2009):

http://cplusplus.github.io/LWG/lwg-active.html#1203

libc++ went outlaw and implemented it anyway.

It is up for reconsideration, but can not possibly be standardized prior to 2017 (standardization is a glacial process).

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577