Now that might be just me but it sounds awfully like a "dey took our jibs!" reasoning there. Must be those filthy Java programmers, et cetera. Also you seem to assume colleges teach "idiomatic Java", I'm going to tell you they don't. They teach generic OO concepts, often badly, no matter the language they end up actually using.
I probably should have been more careful in how I chose my words. Java's never taken my jerb, but I do think its a poor language and yes I do have a bit of a soapbox about it. When I say "idiomatic Java" or "idiomatic C++", I don't necessarily mean the things that really are best practices or what's taught in schools, I mean the things that the culture around that language broadly accepts as sacrosanct and to be revered and striven towards at all levels, not just experts. In java, the enterprise is largely what drives the culture -- Java, the language itself, was literally designed to make Java programmers interchangeable because that's attractive to big business. That's the reason Java gives so much less discretion to the programmer than C++ does, and disallows such anti-social behaviors as operator overloading and the heresy of free-standing functions. Business would prefer if all their programmers wrote the same kind of code, and Java does what it can to enforce that at a language level. Because programmers who have been weened on Java have never been afforded such freedoms, it remains difficult for them to utilize new-found freedoms in a language that affords them the privilege, and so the opportunity is ignored or distrusted by these programmers even when they are objectively a preferable solution.
Some schools, many no doubt, probably don't teach strictly from best-practices. Certainly I was exposed to ideas in my C++ courses that are not good practices for software at scale, usually as a contrived means of demonstrating one language concept within a bubble -- I recall a particular assignment that required us to use exceptions as a flow-control mechanism. But the choice of language does have an impact. In Java, you cannot teach a student that free-standing functions are preferable because the language doesn't truly allow them -- sure, you have static methods, but that starts to fall apart when the static member is equally related to two or more types: where do you put it then? In Java (and in C#) you see people creating new classes whose only purpose is to be a bucket for static methods. How silly is it that one has to pay penance to the type system to achieve such a simple request as a free-standing function? How tragic is it that for most college graduates, this is the solution that becomes their muscle-memory and is something they must unlearn to be an effective C++ programmer. That design decision has nothing to do with good OOP practice, and everything to do with the particular orthodox worldview that Java's proprietors set forth in their market analysis. To be fair, Java serves that market expertly -- it faces essentially no competition from C++ at all in that space, it competes mostly with C# there.
I think the thrust of what I wanted to get across was that to become a truly enlightened OOP practitioner, you have to be willing to be a heretic if your background is Java. It requires you to actively reject certain notions that are enshrined in the Java language. For its part, C++ is not perfect and I'm not saying that teaching C++ in colleges is the cure; I believe C++ to be better in many ways (owing mostly to its lack of far-reaching orthodoxy and its disregard of marketing strategy), but it also presents it own challenges, owing mostly to its C legacy and its incrementally accrued complexity. Its really not that C++ is the ideal expression of OOP, its not, but its the least-restrictive option we have among these three langauges (C++, Java, C#) that are widely used in industry and a broadly-marketable skill. Certainly I am also biased as C++ is the only one among them that lets me express in greatest detail how I want the machine to carry out its work (down to the level of how and when to allocate and reclaim memory, or how to organize and pack data efficiently for machine access) while also giving me the expressiveness to express solutions at a high level in the manner I choose.
Personally, I use exceptions in c++ only for throwing errors from failed constructors. As ctors cannot return a value for obvious reasons this is the common sense way to raise an error. Anywhere else return codes are simpler and easier to work with...
Exceptions, at least as popularly used, are not a laudable feature of C++ or any other language. They're particularly troublesome in C++ though because it lacks a garbage collector (technically, the C++ standard allows for one, but no one offers one I'm aware of). That's why C++ programmers have to deal with this whole business of functions being exception-unsafe, or being exception safe with the weak exception guarantee, or being exception safe with the strong exception guarantee. Return codes are viable when the issue can be dealt with immediately in the calling scope, but they start to break down otherwise -- you end up seeing solutions where the error code is simply stored in some global and a panic() function is called when the error needs to escape more than a couple stack-frames. And every level the "exception" must escape has to have its intent polluted by error handling code. That's actually the reason for exceptions to exist in the first place -- to allow for intermittent errors to be handled without manually accounting for them in the flow-control of your program. If it weren't for the difficulties that stem from the combination of manual resource management and exceptions in C++, exceptions are a fairly good solution for this problem(exceptions work much better in garbage-collected languages like Java or C#), but still always work best when used sparingly.
Also, if we are limiting ourselves to return-codes, how then do functions return other values? Any function that can fail must then use an out-parameter to return its result, which is not convenient and leads to code that can be difficult to follow. In other languages, functions can return multiple values, which alleviates some of this problem, but that's something C++ lacks a good solution for -- it doesn't have multiple return values and lacks a "value bag" type object (such as a tuple) that is both computationally and semantically light-weight (std::tuple is nice for what it is, but isn't so light you'd want to reach for it as the solution to all your essential error-handling). std::option<T> will be sufficiently lightweight I think (when its officially adopted), but its capacity to communicate an error is limited to expressing only whether there was an error or not, it can't on its own express what the nature of the error was.