That's why Rust's Result<T, E> is vastly superior because it out of box pushes the users to very efficient and idiomatic error handling mechanism which is based on its enum types that enjoy a lot of compiler optimizations.
You can also achieve fairly good results with struct-based custom Result<T, E> implementations in C# but until the language gets the support of proper discriminated unions, it will always be inferior.
Thankfully, in C# and, I assume, in other exception-based languages the compiler is smart enough to recognize exceptions as cold paths and correctly reorder emitted code to minimize their impact when you do not throw them. But traversing try-catch blocks still tends to pessimize the codegen significantly hence heavy reliance of C# on various throw helpers to keep hot paths clean.
https://google.github.io/styleguide/cppguide.html#Exceptions
One could argue this is something of a technical debt issue, as the rationale notes:
> "Given that Google's existing code is not exception-tolerant, the costs of using exceptions are somewhat greater than the costs in a new project. The conversion process would be slow and error-prone. We don't believe that the available alternatives to exceptions, such as error codes and assertions, introduce a significant burden."
> "Our advice against using exceptions is not predicated on philosophical or moral grounds, but practical ones. Because we'd like to use our open-source projects at Google and it's difficult to do so if those projects use exceptions, we need to advise against exceptions in Google open-source projects as well. Things would probably be different if we had to do it all over again from scratch."
"On their face, the benefits of using exceptions outweigh the costs, especially
in new projects. However, for existing code, the introduction of exceptions has
implications on all dependent code. If exceptions can be propagated beyond a
new project, it also becomes problematic to integrate the new project into
existing exception-free code. Because most existing C++ code at Google is not
prepared to deal with exceptions, it is comparatively difficult to adopt new
code that generates exceptions." (https://google.github.io/styleguide/cppguide.html#Exceptions)
So, basically, Google forbids exceptions for historical reasons, not because of performance.But, sadly, countless companies parroted this section of Google's style guide for all the wrong reasons (mostly just cargo culting Google) leading to unfortunate fragmentation of error handling in the C++ library ecosystem.
Anyway, if the callee throws an exception, it will be just as costly as any other even if you can examine the `Value/Task<T>` for `.IsFaulted` and `.Exception`.
Possibly counterintuitively, if thrown, it will also dwarf the overhead of allocating state machine object and executing `MoveNext()` decoration. Hence even in async there is value to be had in not using exceptions in performance-sensitive scenarios.
Spoiler, each throw/catch costs 20 microseconds
I generally consider exceptions in C++ harmful and avoid them if possible. It is not, unfortunately, always possible, as exceptions are the only way the language defines for constructors to fail.
Once this decision was made, investing time in optimizing programs that use exceptions for regular control flow became very very low in the priority list of all C++ compilers. This would include recognizing cases where an exception could be replaced with a single if/else, and optimizations for the unhappy path in general.
I don’t think there’s anything in the standard that prevents the compiler from optimizing away said exception completely, but neither gcc or clang do so even in extremely trivial cases where the exception is identical to an if statement: