7
class foo {
    public:
    friend ostream& operator << (ostream &os, const foo &f);
    foo(int n) : a(n) {}
    private:
    vector <int> a;
};

ostream& operator << (ostream &os, const foo &f) {
    for (int i = 0; i < f.a.size(); ++i)
        os << f.a[i] << " ";
    os << endl; // why is this line a must?
}

int main(void) {
    foo f(2);
    cout << f << endl;
    return 0;
}

In the above code, if the marked line is removed, there will be a segment fault error, can someone explain why?

shilk
  • 589
  • 5
  • 17
  • 1
    Why nobody ever cares to check the code in question? Compiler should have warned you about such error -"... warning: no return statement in function returning non-void [-Wreturn-type]" See http://liveworkspace.org/code/2ygK20$1 } ^ – SChepurin Apr 03 '13 at 13:48

1 Answers1

19
ostream& operator << (ostream &os, const foo &f) {
    for (int i = 0; i < f.a.size(); ++i)
        os << f.a[i] << " ";
    os << endl; // why is this line a must?
}

is not manadatory. The segfault is caused because you are not returning os

ostream& operator << (ostream &os, const foo &f) {
    for (int i = 0; i < f.a.size(); ++i)
        os << f.a[i] << " ";
    return os; // Here
}

it is undefined behavior if you don't return the ostream. The endl is flushing your os here. That's why it seems like it is working.

EDIT: Why it is working in this case according to Bo Persson

The os << endl; is another operator call that actually returns os by placing it "where a return value is expected" (likely a register). When the code returns another level to main, the reference to os is still there

  • Is the implicit `int` (?) which would be returned if nothing is specified or its value somehow standardized? – Benjamin Bannier Apr 03 '13 at 13:34
  • 4
    There is no implicit `int` returned here. It is quite clearly specified as returning `ostream&`. So without a `return` statement you get whatever junks happens to be in the right place on the stack after the function. It just so happens that with the `os< – BoBTFish Apr 03 '13 at 13:37
  • 2
    @honk There is no implicit `int` in C++. Failing to return something from a non-`void` function is simply undefined behaviour, period. – Angew is no longer proud of SO Apr 03 '13 at 13:37
  • Thanks @Angew, that's the answer to my question. – Benjamin Bannier Apr 03 '13 at 13:40
  • 1
    @stardust_ *"The endl in this case..."* That's not really true. That line is likely placing `os` where the return value is expected. By coincidence. – Drew Dormann Apr 03 '13 at 13:41
  • @BoBTFish Wait... is that always true for any function? You don't *have* to return what you said you would? It's the same as just leaving a variable uninitialized (or UB)? Isn't it a compile error? – David Apr 03 '13 at 13:41
  • @Dave: This would trigger a warning e.g. in GCC with `-Wall`. – Benjamin Bannier Apr 03 '13 at 13:43
  • 2
    @Dave You do *have* to. Just some compilers with whatever options might not tell you that you've done it wrong. – BoBTFish Apr 03 '13 at 13:44
  • Well that's stupid for any compiler not to treat it like an error. There's no edge case where it's not an error. It's definitely wrong – David Apr 03 '13 at 13:45
  • @Dave. the thing is it is not technically an error if you don't use the return value. If you jsut call the function and not assign the result to anything. However in an ostream case you are always using what is returned (because of the syntax). –  Apr 03 '13 at 13:48
  • 3
    From N3337 (first draft after c++11) `6.6.3`: "Flowing off the end of a function is equivalent to a `return` with no value; this results in undefined behavior in a value-returning function." I guess the reason a compiler might not consider this an error is that there are other legal ways to exit a function (e.g. throwing an exception). – BoBTFish Apr 03 '13 at 13:56
  • 5
    @Dave - The `os << endl;` is another operator call that actually returns `os` by placing it "where a return value is expected" (likely a register). When the code returns another level to main, the reference to `os` is still there. – Bo Persson Apr 03 '13 at 13:58
  • 1
    @Dave: `int foo() { for (;;) { int i; if (std::cin >> i) { return i; } std::cin.clear(); } }` is a perfectly conforming function even though there is no `return` at the end. It is only undefined behavior if you exit the function "normally" without returning. Compilers do not make this a hard error because they cannot, in general, prove that you will go down this path; after all you may be calling a function that `throw` or something... – Matthieu M. Apr 03 '13 at 14:29
  • @MatthieuM. So is it UB by exiting without a return or is it only UB if you try to use the return value from a function didn't return anything? – David Apr 03 '13 at 14:53
  • 2
    @Dave: in C it's UB if you try to use the value, in C++ it's UB if you "fall off" the end of the function without returning. – Matthieu M. Apr 03 '13 at 15:12