How do I make an expression non-movable? What’s the opposite of std::move?

Raymond Chen

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());

5 comments

Discussion is closed. Login to edit/delete existing comments.

  • Neil Rashbrook 0

    Why std::remove_reference_t<T> const& rather than just T const&?

    • Daniel A 0

      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 reference reference 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.

  • alan robinson 0

    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?

    • 紅樓鍮 0

      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 they’re the first widely-used industrial programming languages as well as the first imperative programming languages that implemented the respective concepts).)

      A lot of people (there’s been more and more of them recently) have started to become disappointed in OOP, and most languages today (more honestly, all languages since Go that I can think of) are not designed around the OOP principle anymore. They usually support something similar to OOP’s dynamic dispatch, such as Go’s interfaces and Rust’s trait objects, but I doubt you’ll find inheritance in any of the newer languages anymore.

Feedback usabilla icon