Exceptions cause all sorts of problems(like violating the principal of only paying for what you use). They aren't checked so you can't enforce handling via type system and in general they're just a mess in C++.
Exceptions cause all sorts of problems(like violating the principal of only paying for what you use). They aren't checked so you can't enforce handling via type system and in general they're just a mess in C++.
Exceptions are zero-cost in terms of performance on any sane architecture (this excludes x86, but not x64). The only cost that you're paying is the extra memory for the tables, which are all statically allocated.
I don't really see this as much of an issue given you have to have this RTTI to catch the exception properly.
If we consider the combo of exceptions+RTTI, then yes, dropping both at the same time lets you get rid of some further overhead (although again it's memory-only, not compute cycles). But then we have to discuss the lost benefits of those other features, not just exceptions alone, to judge whether doing so is worthwhile or not.
BTW, I may well be wrong here, but isn't RTTI generation for exceptions (when it's otherwise disabled) an example of "only pay for what you use"? i.e. wouldn't the compiler then only generate RTTI entries for those types that are either thrown somewhere, or are caught somewhere? If so, any library code that doesn't throw itself, and only needs to allow exceptions to pass through it, is still zero-overhead in that regard.
OTOH the library always has the option to say "this particular thing that you give me should be nothrow; if it throws, that's U.B.", and not even try being defensive in the face of exceptions when they come out of such a thing.
If you use optional<T> you have to explicitly write that, and turning off exceptions means you don't have to write noexcept everywhere.
In statically typed languages, return values, like (fully) checked exceptions, are different, as they're exactly the sort of global information the compiler needs. Functions can statically guarantee the exact set of ways in which it can interact with its caller, and the compiler can understand. This means a function that, say, sorts a list with a less-than function (that returns bool) knows that every time that function exits, it gives a value of true or false, no exceptions (hah).
In any case, just writing a library that says "UB to throw" isn't quite enough, as the compiler won't understand, and will still structure code to handle exceptions "sensibly", even thought it doesn't need to. Fixing that means being able to communicate nothrow to the compiler (which is nice too), but this starts to no longer be a pervasive unchecked error system: it's somewhat of a binary form of checking.
For example the following code will compile differently depending on whether goo() can throw.
struct k { void p[4]; };
void goo(k s);
k foo() { k r; k s; goo(&s); r = s; return r; }
If goo() doesn't throw the extra variable s can be eliminated.
Although I still don't see why, in your example, one of the variables cannot be elided even if goo does throw. Since there's no point at which address of r is taken, much less escapes the scope, it should always be safe to elide r and just return s, no?
Not when the exception is triggered
(Of course you could do the same thing as Rust and expose only a static creator function and hide the actual constructor.)
Honestly most error codes are small enough that you can just pay the hit for it on the stack and discard it once you've handled the good/bad case.
boost::variant otherwise, but depending on the types used (if there aren't any that are nothrow default constructible) it allocates on assignment.
If you just want a result type and not a more general "rust enum", this repository does it (while only allocating space for the larger, I think) https://github.com/oktal/result/
And I don't think there's any reason you couldn't implement an equivalent of std::variant for previous versions of C++, I'm just not aware of any.