You can use std::move
to indicate that a value can be moved. But what can you do to indicate that something movable cannot be moved after all?
For example, maybe you want to force the copy assignment operator to be used instead of the move assignment operator:
extern Thing GetThing(); Thing thing; // this uses the move assignment operator thing = GetThing();
You might want to force the copy assignment operator because the copy assignment operator comes with additional statistics or bookkeeping. How do you make something non-movable?
You can do it by const-ifying the thing, so that the only available option is to copy it.
template<typename T> std::remove_reference_t<T> const& no_move(T&& t) { return t; }
The no_move
function takes any kind of reference, and then returns a const
reference to that referred-to thing.
extern std::vector<int> make_vector(); // Force copy assignment, not move assignment. v = no_move(make_vector());
Bonus chatter: Note that the following similar-looking code is not a move operation:
std::vector<int> v = make_vector();
The above code does not construct v
by moving the result of make_vector
. Rather, the above code uses copy elision, and the return value of make_vector
is placed directly into v
without any copying or moving at all.
But the same trick can be used to suppress copy elision.
// Force copy constructor, not move constructor copy elision.
std::vector<int> v = no_move(make_vector());
Am I the only one who feels like C++ is trying to be a higher level language than it should be? Yikes, the ever expanding scope of the language makes it feel like it would take TWO lifetimes to master CPP23 from scratch, during which 4 more lifetimes of features would be added to the standard. Could somebody just add classes to C (call it, umm, C with Classes) and then promise to stop the never-ending language expansion?
My personal experience learning Modern C++ is that Modern C++ is actually closer to being a functional programming language in disguise, rather than an object-oriented programming language. Modern C++ uses template-based static polymorphism (very similar to OCaml's parametric polymorphism) in place of inheritance and dynamic dispatch, and uses value semantics and move semantics to reduce sharing (and thus cross-thread sharing) of mutable data.
(Note that move semantics is not something common to FP languages; in fact, C++11 basically "invented" move semantics, in the same sense that C# "invented" async/await (the features has existed in some obscure, arcane FP languages before, but...
Why
std::remove_reference_t<T> const&
rather than justT const&
?Because T may be deduced to be a reference-type and reference-types are const by default (i.e. you cannot change where the reference is pointing). In such case the const keyword is ignored and you end up with a function that returns a
non-const referencereference to non-const.Here’s a comparison: https://godbolt.org/z/dYnE1nzjG
From there you can go to CppInsights to see what functions were generated from the templates.
Related (in C++23): auto(x): decay-copy in the language, https://wg21.link/P0849
Example: https://en.cppreference.com/w/cpp/language/explicit_cast#Example