> This language feature is best understood as an attempt to mitigate the complexity of this other language feature
That doesn't really make any sense. Move semantics aren't meant to mitigate complexity of function overloading. It's more like, it uses function overloading as part of its implementation.
I do strongly prefer Rust's strategy of move being the first class citizen (and being destructive), with copy (/clone) layered over the top. But of course C++ got where it is for historical reasons. And I've never used Rust properly so I don't really know if the grass is greener.
> Rust isn't as far back on that road is it pretends and is likely catching up.
The grass is definitely greener. In Rust you never have any doubts about whether you're moving or copying. Although...
Rust's 'move semantics' are defined as copying the value, including any internal references. That's also what happens in C++, of course, and normally 'the value' is a small struct with potential heap references embedded. But it's possible to embed a large array in a struct, in which case moving it is expensive-
Except usually not, as the memory copy only exists in principle and the optimiser will usually remove it. But that isn't visible at the language level.
It's also nice that the moved-from value cannot be used afterwards. No need to wonder if it's in a sensible state or not -- the type system won't let you access it. (Which is why the optimiser is fairly reliable at reusing the data.)
> It's also nice that the moved-from value cannot be used afterwards.
There are a few cases in low-level systems code where being able to defer destruction of the moved-from value is useful. A common example is when there is no way to enforce ownership semantics on the resource at compile-time e.g. direct I/O or similar types of shared address space. Being able to safely move the value immediately does not imply that you can safely destroy the moved-from value immediately as long references not visible from your address space exist.
The non-trivial moves in C++ are convenient for wiring in the necessary mechanics, automagically ensuring the moved-from value lives long enough for the exogenous references to discover it has been moved. You can do this manually without non-trivial moves but the syntactic sugar makes some things that are inherently memory-dangerous much safer.
> Except usually not, as the memory copy only exists in principle and the optimiser will usually remove it. But that isn't visible at the language level.
that's the biggest difference form C++: a copy or move can, in principle, have observable side effects, so, aside from specifically santioned copy/move elision scenarios, the compiler cannot elide a copy or move constructor unless it can prove there are no visible side effects (usually by inlining the copy/move constructor and optimizing it away).
On the other hand this allows more flexible copy/move behaviour beyond just handling memory.
unneeded complexity is bad. But making simplicity out of a complex problem takes more time and talent than most people have (if it's possible at all for that problem space to begin with).
C++ and Rust are designed around dealing with complex problems. Specifically in this case where memory management is of utmost importance. Understand the problem you're trying to solve before dismissing a tool as "too complex". If you don't have that problem, wonderful. More time to focus on the problems you actually need to solve instead of complaining about other problems.
C++ is definitely complex, probably too complex, but "This language feature is best understood as an attempt to mitigate the complexity of this other language feature" is, aside from being wrong, needlessly inflammatory.
Prima facie: what does std::move look like it's trying to band-aid fix? copy operations?
They expanded the Rule of 3 to the Rule of 5 for a reason. The operations work together. Move semantics simply work as an optimization when you know the data you're taking in won't be needed anymore and you can more or less "snatch" the data (to put it overly broadly). If you can't do that, you take a slow path and just copy properties one by one.
Garbage collection? Well, besides these languages not having that, managed languages are making the exact same decisions under the hood when optimizing. if it knows a reference is being assigned but then goes out of scope, it can do similar move semantics to save on a copy.
Sigh. That statement was literally a very straightforward paraphrase[1] of the upthread point, which I even quoted. That one seems "inflammatory" and the other doesn't speaks more to the reader's perspective than the text's.
But by all means, let's not debate programming language complexity and call people vacuous trolls instead.
[1] "explanations fail because they don't focus on" -> "best understood as". I mean, come on. I'm not reading much bile here. You added that.