One tiny baby step towards functional programming in C++ is to avoid assignment statements as much as possible and instead use const initialization:
const type foo = value;
Assignment statements mutate state, whereas 'foo' above is an alias or cached value which can not mutate state. This is much like the ‘let’ keyword in Lisp or Swift. It is much easier for both the programmer and the compiler to reason about non-mutating expressions. Many compiler suites (e.g., LLVM) use static single assignment (SSA) for their intermediate form which opens the door for a lot of optimization. Anyway, it is a small thing and can act as a "canary in the mine" if you find you can not refactor your code into this form.
There are some corner-cases where immutable data still fails algorithmically... for example I do not know of a performant quicksort that is written in an immutable language (but please, someone correct me if I'm wrong!). It's so wrong, too, because the code for functional quicksort is so beautiful... here it is in Haskell
Here's the correct version taken from one of the SO answers :
import qualified Data.Vector.Generic as V
import qualified Data.Vector.Generic.Mutable as M
qsort :: (V.Vector v a, Ord a) => v a -> v a
qsort = V.modify go where
go xs | M.length xs < 2 = return ()
| otherwise = do
p <- M.read xs (M.length xs `div` 2)
j <- M.unstablePartition (< p) xs
let (l, pr) = M.splitAt j xs
k <- M.unstablePartition (== p) pr
go l; go $ M.drop k pr
Well according to the reasoning here, no purely functional quicksort will ever be "true quicksort" due to the apparent requirement that the sort be done in-place (mutably). Haskell may have well-thought-out mechanisms in place to do mutation "purely" (which are quite admirable IMHO) but this won't be the case with something like Elixir (Erlang).
This code is also considerably more complex/less beautiful.
I don't know what to call non-in-place Quicksort, but it deserves a name; in the meantime I will simply put it in quotes.
It does seem possible to write an Elixir "quicksort" that does not consume gobs more space than "proper" quicksort, based on the comments there.
I am not sure what you mean by "fails algorithmically," but I do know functional code that performs no (logical) mutations is often dependent on a really smart compiler to do "the right thing" (e.g. convert tail recursion to iteration, re-use memory, etc..).
A "functional" quick-sort in C++ that is also performant sounds like a hard challenge. In this case I am no purest -- I'll use a mutating version -- I suppose that is why Common Lisp provides a destructive version of sort.
If you do create a non-destructive quick sort, you get reentrancy for free so it is easily parallelizable. Writing concurrent routines using non-mutating code (as Carmack says) is much easier to get right.