The flow insertion operator cascade does not work


I was reading an article about Overloading the Stream Insertion Operator (<<). It stresses that the output stream object ought to be returned to ensure the operator is cascaded correctly. But it seems without the return, the output is still correct, what's wrong here?


class Rational
    friend std::ostream& operator<<(std::ostream&, const Rational&);

        int numerator;
        int denominator;
        Rational(int num, int den): numerator{num}, denominator{den} {}

std::ostream& operator<<(std::ostream& lhs, const Rational& rhs)
    lhs << rhs.numerator << "/" << rhs.denominator;
    //return lhs;

int main()
    Rational r1(3, 5);
    std::cout << "The value of r1 is " << r1 << std::endl; // After commenting return lhs; still works fine

This is UB, for a function that is supposed to return an object ends without a return statement. It might work well but nothing is guaranteed.

From the standard, $6.6.3/2 The return statement [stmt.return]:

(emphasis mine)

Flowing off the end of a constructor, a destructor, or a function with a cv void return type is equivalent to a return with no operand. Otherwise, flowing off the end of a function other than main (basic.start.main) results in undefined behavior.

You might want to see the result from clang; which gives a warning

warning: control reaches end of non-void function [-Wreturn-type]

and results in an infinite recursion.