I assume that you understand that both functions crash due to the exhaustion of the stack after trying infinite recursion. I think you are asking: why does the cout example not crash with "Stack Overflow" as well?
I do not think the answer is related to the discovery of a tail recursion compiler. If the compiler optimized recursion, none of the examples should crash.
I have an assumption about what is happening. The exception of "stack overflow" is implemented in some cases (for example, Windows) with one "protective page" of virtual memory allocated at the end of the stack. When access to the stack hits this protection page, a special type of exception is created.
Since Intel's low-detail page is 4096 bytes long, the security page is on guard in the range of memory size. If a function call allocates more than 4096 bytes of local variables, it is possible that the first access to the stack from it will actually stretch outside the protective page. It is expected that the next page will be saved unprotected memory, so in this case it makes sense to violate access rights.
Of course, you are not explicitly declaring any local variables in your example. I would suggest that one of the operator <() methods allocates more than a page of local variables. In other words, access violation occurs at the beginning of the operator <() method or some other part of the cout implementation (temporary object constructors, etc.).
In addition, even in the function you wrote, operator <() implementations will need to create some storage for intermediate results. This repository is probably allocated by the compiler as a local repository. I doubt this will add up to 4k in your example.
The only way to figure this out is to see the stack trace of the access violation to see which instruction launches it.
Got a stack trace of access violation and disassembly around the area of the failure operation code?
If you use the Microsoft C compiler, another possibility is that printf () and your own function were compiled with / Ge, and operator <(lt) () was not, or that only your function was compiled with / Ge and factors similarly described above, by default they invoke the behavior that you see - because the printf () example crashes when the function is called, and in the case of the <() operator, when you call the library.