Thread: Non-member operator acting on temporary objects: why doesn't this compile?

  1. #1
    Registered User
    Join Date
    Mar 2002
    Posts
    125

    Non-member operator acting on temporary objects: why doesn't this compile?

    It's been a long time since I've done any C++ programming, but I've started back up and for the most part, things are going well. This problem however, has me stumped. Basically, I'm working with the wxWidgets library which provides a class "wxString" that works like a std::string except it also provides a stream-like overloaded << operator to concatenate non-string values at the end of the string. If, for example, you have a logging function that accepts a wxString it lets you do stuff like:
    Code:
    void Log(wxString const& in) {...}
    
    class Foo
    {
        int bar;
      public:
        LogBar()
        {
            Log(wxString("Bar is ") << bar << " right now.");
        }
    }
    However when I try to define my own overloaded << operator for my own classes, things go wrong, but ONLY if I use it on a temporary object like above. Very much condensed (and non-wxWidgets specific) here's a complete program that causes the problem:

    Code:
    #include <iostream>
    #include <string>
    
    class StreamString
    {
    	public:
    		std::string buffer;
    		StreamString() {};
    };
    
    class ThingToPutInStream
    {
    	public:
    		ThingToPutInStream() {}
    };
    
    
    StreamString& operator << (StreamString & stream, ThingToPutInStream const& in)
    {
    	stream.buffer += "ThingToPutInStream";
    	return stream;
    }
    
    void OutputStreamString(StreamString const& in)
    {
    	std::cout << in.buffer;
    }
    
    int main()
    {
    	{
    		StreamString foo = StreamString();
    		OutputStreamString(foo << ThingToPutInStream());
    	}
    	// these four lines compile normally
    
    	OutputStreamString(StreamString() << ThingToPutInStream());
    	// this line fails to compile
    	return 0;
    }
    Which gives the error: (compiled with mingw32-g++ 3.4.5)

    Code:
    C:\Stuff\temptest.cpp||In function `int main()':|
    C:\Stuff\temptest.cpp|37|error: no match for 'operator<<' in 'StreamString() << ThingToPutInStream()'|
    C:\Stuff\temptest.cpp|19|note: candidates are: StreamString& operator<<(StreamString&, const ThingToPutInStream&)|
    ||=== Build finished: 1 errors, 0 warnings ===|
    As far as I can tell the two bits of code in the main function are functionally equivalent; so why does one give me that error?
    I'm obviously missing something here and I get the feeling it's something obvious, but I can't figure out. How do I get this operator to work with temporary objects?
    Thanks in advance!
    Typing stuff in Code::Blocks 8.02, compiling stuff with MinGW 3.4.5.

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    StringStream() is a const StringString& -- because, as you note, it is a temporary -- and as such won't match a "plain" StreamString&.

    Forgot to mention: That means that, as it stands, you have no chance of getting your function to work with a temporary object. Can you put your message in the constructor of the object instead (i.e., StringStream(ThingToPutInStringStream))?

  3. #3
    Registered User
    Join Date
    Mar 2002
    Posts
    125
    Ah, it never occurred to me that temporaries would by definition be a bizarre kind of maybe sort of const-ish, and for some reason the compiler error didn't say anything like that either. After some more research into the whole temporaries being const business, it turns out that you are allowed to call non-const member functions on temporary objects (which is why the code in the first block in my first post did compile) but you're not allowed to pass them to functions that take non-const references. Weird. Anyway, at least I now know that this isn't a trivial problem and actually has its roots in some part of the C++ standard I don't fully understand.

    Quote Originally Posted by tabstop View Post
    Can you put your message in the constructor of the object instead (i.e., StringStream(ThingToPutInStringStream))?
    Sadly, the reason I wanted to do this with an external operator is because in the real-world scenario, I'm not the one that made the "StreamString" class (i.e. wxString, which is part of the wxWidgets library), so I cannot change it in any way. My hope was to be able to provide an external operator overload in the same way you would do for std::ostream to let you easily pass your own classes to std::cout or a file stream, but it seems that if I want to go that way for wxString, I have no choice but to create a non-temporary wxString on the stack each time I want to output one of my own classes. It's not pretty, but it'll have to do. At least it works. (alternatively, I could just brute force my way past the restriction and cast a const reference to a non-const one but that seems a bit dangerous, especially with the knowledge that I don't fully understand why it has to be a const reference)

    Anyway, thanks for the help!
    Last edited by Boksha; 06-06-2011 at 02:52 PM.
    Typing stuff in Code::Blocks 8.02, compiling stuff with MinGW 3.4.5.

  4. #4
    Registered User
    Join Date
    Mar 2002
    Posts
    125
    After giving it some more thought I realized that there is a way to still make this work by overloading the operator for a const left member and having that version return by value instead of reference. It's probably slower than working with references, but for non-time essential stuff that's not a problem, and it'll work without actually having to change anything in the calling code.
    Code:
    StreamString &operator << (StreamString & out, ThingToPutInStream const& in)
    {
    	out.buffer += "ThingToPutInStream";
    	return out;
    }
    
    StreamString operator << (StreamString const& stream, ThingToPutInStream const& in)
    {
    	StreamString out;
    	out.buffer = stream.buffer;
    	out.buffer += "ThingToPutInStream";
    	return out;
    }
    I hope this won't come biting me in back later...
    Typing stuff in Code::Blocks 8.02, compiling stuff with MinGW 3.4.5.

  5. #5
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    I actually think that declaring operator << like that makes more sense than how C++ iostreams do it. It is then consistent with how other ordinary classes would perform operator <<.
    The way iostreams work, they really should have their operator<< renamed to be operator<<=, because that's what their actual behaviour is like.
    But hey, pretty syntax is obviously more important than consistency...
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  6. #6
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    I'd just eliminate the temporary object to be honest, but that's just my $.02. The whole situation feels like unnecessary shenanigans.

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by iMalc View Post
    I actually think that declaring operator << like that makes more sense than how C++ iostreams do it. It is then consistent with how other ordinary classes would perform operator <<.
    The way iostreams work, they really should have their operator<< renamed to be operator<<=, because that's what their actual behaviour is like.
    But hey, pretty syntax is obviously more important than consistency...
    How is the stream's interpretation of the shift operators consistent with anything, whether it's << or <<=? Anyway, <<= binds right-to-left, so have fun writing this:
    Code:
    (((cout <<= "Hello, ") <<= name) <<= "!") <<= endl;
    Also, streams are non-copyable, so having a const lhs argument simply doesn't work.

    R-value references ought to improve the situation.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #8
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by CornedBee View Post
    How is the stream's interpretation of the shift operators consistent with anything, whether it's << or <<=? Anyway, <<= binds right-to-left, so have fun writing this:
    Code:
    (((cout <<= "Hello, ") <<= name) <<= "!") <<= endl;
    Also, streams are non-copyable, so having a const lhs argument simply doesn't work.

    R-value references ought to improve the situation.
    It could be consistent with regards to the constness of the arguments, if it overloaded operator<<= instead, which would make sense since it is a modifying operation. However yes, it the usage of it would be horrible.
    Really, the language could have done with a separate operator for stream input e.g. <- but there's dowsides to yet another operator as well.

    I'd like to clarify my statement above though, although how he has it here "makes more sense" I mean that only in terms of how the operator is defined with regards to the constness. In all practically it is not at all nice to have to return a copy, and as noted, not possible with real streams.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Complicated member objects that depend on `this`
    By drrngrvy in forum C++ Programming
    Replies: 3
    Last Post: 11-12-2006, 09:48 PM
  2. Doesn't Compile
    By Justin C in forum C++ Programming
    Replies: 12
    Last Post: 04-29-2005, 06:40 PM
  3. Replies: 3
    Last Post: 03-07-2003, 09:06 PM
  4. defining objects in a non-member operator?
    By pkananen in forum C++ Programming
    Replies: 5
    Last Post: 12-13-2001, 12:17 PM