views:

224

answers:

3

Hi, I tried the following code:

#include <iostream>
using std::cout;
using std::ostream;

class X
{
public:
    friend ostream& operator<<(ostream &os, const X& obj) 
    {
        cout << "hehe";          // comment this and infinite loop is gone
        return (os << obj);
    }
};

int main()
{
    X x;
    cout << x;
    return 0;
}

When I compile & run this, it's as expected; an infinite loop. If I remove the cout statement inside the friend function, the recursion doesn't happen. Why is it so?

+5  A: 

In both cases (with and without writing "hehe") Visual Studio 2005 gives the following warning:

warning C4717: 'operator<<' : recursive on all control paths, function will cause runtime stack overflow

In both cases it compiles and in both cases it gives a stack overflow.

However, without the "hehe" the stack overflow occurs a bit sooner.

Patrick
You're right Patrick, thanks! And thanks to litb for doubting the validity of my conculsion that recursion doesn't happen, which made me run it via `gdb` and now I'm satisfied to see a `SIGSEGV` which proves that in both cases a recursion occurs :)
legends2k
A: 

In your code:

return (os << obj);

is causing the recursion. Replace it with:

return os;

and you should be fine.

Ton van den Heuvel
Please read the question properly. The problem isn't about recursion, it's about why it _isn't_ happening when I remove the `cout<<"hehe";`.
legends2k
That was not the question. The question was "why doesn't the stack overflow occur if the 'cout << "hehe"' statement is omitted? (which isn't the case)
Patrick
+3  A: 

Optimizer decides all your remaining activity has no effect and optimizes it away. Whether it's right or wrong is a different matter.

In particular:

X x;

creates empty object "x"

cout << x;

calls:

return (os << obj);

which is appending empty object; the compiler notices 'os' hasn't grown any since the last call and shows no promise doing so any further (and nothing else happens) so it decides the whole business is redundant and can be truncated at this point.

In case you call

    cout << "hehe";          // comment this and infinite loop is gone

there is some extra activity so the optimizer doesn't remove the following call.

I guess if you initialized x with anything non-empty, or performed any non-null activity other than cout << "hehe";, you'd have recursion running just the same.

SF.
Actually I think that the optimizer would be perfectly right if the standard specified that infinite recursion causes "undefined behavior"; doing nothing at all would be OK in that case. I can't find a copy of the C++ standard on the net, can anyone who have it check?
Matteo Italia
...on unrelated note, I wonder what if the call was enclosed in `try-catch(...)` to capture stack overflow exception. Which, I think, would be pretty much the expected behavior, correct me if I'm wrong.
SF.
I don't think that such exception (that, by the way, is not a C++ exception, but it's some kind of signal on *NIX and a SEH exception on Windows) can be caught. Actually, there's nothing you can do when the stack overflows: everything has already gone nuts. If you notice, Windows applications that go in stack overflow do not even display an error message, because they can't do anything now that there's no more stack.
Matteo Italia