Pure functions are appropriate if all you want to do is math. |
The premise of your first paragraph is wrong. There are a number of fundamental counterexamples in the standard library itself (e.g., iterators), not to mention other components (variant, transform, sort, reduce, hash, ...) and external libraries (Boost.Spirit, Ranges v3) which use declarative and functional programming idioms to great effect.
Exception safety is affected more by the choice of operations in a function and the order in which they occur than by side effects. |
Functions without side effects rarely need to worry about exception-safety.
Functions with side effects often need to worry about exception-safety. For those functions with side effects, it's quite difficult to provide rollback semantics (called the "strong exception guarantee")
https://en.wikipedia.org/wiki/Exception_safety
Without either
a.) making a copy of the original mutable data; or
b.) never modifying the original data at all
Requiring an explicit copy just makes the current idioms less convenient and more error-prone. (See copy-and-swap, e.g.)
The * and & are easy to miss or include by mistake. Itsy bitsy details like that are the greatest source of bugs. |
They are potentially a source of bugs. I'm not certain that details like these are "the greatest source of bugs"; In most cases static typing would prevent run-time errors, especially in a const-correct codebase.
Would it be preferable if the core language made such typos harder to miss? Perhaps.
1 2
|
#define REFERENCE &
void incf(int REFERENCE x) { x++; }
|
As long as a way to pass arguments by value remains in the language, the only differences are merely cosmetic. It makes more sense for pass-by-value to remain the default for the reasons I outlined in my previous post.