128

I was surprised this didn't show up in my search results, I thought someone would've asked this before, given the usefulness of move semantics in C++11:

When do I have to (or is it a good idea for me to) make a class non-movable in C++11?

(Reasons other than compatibility issues with existing code, that is.)

Community
  • 1
  • 1
user541686
  • 189,354
  • 112
  • 476
  • 821
  • When a class has std::mutex, should make it non-movable? – billz Jan 13 '13 at 11:24
  • 3
    boost is always one step ahead - "expensive to move types" (http://www.boost.org/doc/libs/1_48_0/doc/html/container/move_emplace.html) – SChepurin Jan 13 '13 at 11:34
  • With regards to std::mutex [this thread](http://stackoverflow.com/questions/7557179/move-constructor-for-stdmutex) is useful information and possibly answers your question, in an indirect way, too. – Steve Jan 13 '13 at 11:42
  • Great example @billz, I hadn't thought of threading; please post it as an answer! Are there any other situations too though, if I'm not doing any threading? – user541686 Jan 13 '13 at 11:50
  • 1
    @SChepurin: "Expensive to move" isn't terribly clear... I mean, if something is too expensive for your use case then you should probably avoid it, whether it's in real life or in programming or in a game :-) It doesn't tell me anything I didn't already know. – user541686 Jan 13 '13 at 11:51
  • @Steve: Yup, billz just pointed that out; that's one situation, would love to know if there are others (where threading/synchronization isn't the problem issue). – user541686 Jan 13 '13 at 11:52
  • @Mehrdad - Thus, they tried to avoid it. It means somebody (boost) already "asked this before". – SChepurin Jan 13 '13 at 12:02
  • 1
    I think this is a very good and useful question (`+1` from me) with a very thorough answer from Herb (or his twin, [as it seems](http://stackoverflow.com/questions/14302834/when-to-make-a-type-non-movable-in-c11#comment19868445_14303116)), so I made it an FAQ entry. If someone objects just ping me at [the lounge](http://chat.stackoverflow.com/rooms/10/loungec), so this can be discussed there. – sbi Jan 13 '13 at 12:25
  • 2
    AFAIK movable classes can still be subject to slicing, so it makes sense to forbidding moving (and copying) for all polymorphic base classes (i.e. all base classes with virtual functions). – Philipp Jan 13 '13 at 18:15
  • Can we please clarify what "movable" means exactly? Does "movable" mean that there is a move ctor or just that a move request is legal (and might fall back on copying)? – sellibitze Jan 14 '13 at 09:53
  • @sellibitze: I don't understand the distinction. What kind of a type would have a copy constructor (or assignment operator) but not a move constructor (or assignment operator)? I always thought that a type which is copyable would also turn out to be movable... – user541686 Jan 14 '13 at 09:57
  • 1
    @Mehrdad: I'm just saying that "T has a move constructor" and "`T x = std::move(anotherT);` being legal" are not equivalent. The latter is a move-request which might fall back on the copy ctor in case T has no move ctor. So, what does "movable" mean exactly? – sellibitze Jan 14 '13 at 10:16
  • @sellibitze: T has a move-constructor. – user541686 Jan 14 '13 at 10:18
  • "_What kind of a type would have a copy constructor [...] but not a move constructor [...]?_" -- One written for C++03 and not updated yet. Such a type is MoveConstructible and MoveAssignable according to the standard, because a copy is a degenerate move. – Jonathan Wakely Jan 14 '13 at 13:18
  • 1
    @Mehrdad: Check out the C++ standard library section on what "MoveConstructible" means. Some iterator might not have a move constructor, but it still is MoveConstructible. Watch out for varying definitions of "movable" people have in mind. – sellibitze Jan 15 '13 at 09:13
  • 1
    How about "because you don't want to care to prove that moving is well defined for your type" does it count ? you just make it nonmovable until needed. when it is, you implement move semantic correctly. I'm just guessing though. but in C++03 i have a habit of making all my types noncopyable by default, simply to code less and care less. because YAGNI. – v.oddou Apr 16 '15 at 01:35

4 Answers4

111

Herb's answer (before it was edited) actually gave a good example of a type which shouldn't be movable: std::mutex.

The OS's native mutex type (e.g. pthread_mutex_t on POSIX platforms) might not be "location invariant" meaning the object's address is part of its value. For example, the OS might keep a list of pointers to all initialized mutex objects. If std::mutex contained a native OS mutex type as a data member and the native type's address must stay fixed (because the OS maintains a list of pointers to its mutexes) then either std::mutex would have to store the native mutex type on the heap so it would stay at the same location when moved between std::mutex objects or the std::mutex must not move. Storing it on the heap isn't possible, because a std::mutex has a constexpr constructor and must be eligible for constant initialization (i.e. static initialization) so that a global std::mutex is guaranteed to be constructed before the program's execution starts, so its constructor cannot use new. So the only option left is for std::mutex to be immovable.

The same reasoning applies to other types that contain something that requires a fixed address. If the address of the resource must stay fixed, don't move it!

There is another argument for not moving std::mutex which is that it would be very hard to do it safely, because you'd need to know that noone is trying to lock the mutex at the moment it's being moved. Since mutexes are one of the building blocks you can use to prevent data races, it would be unfortunate if they weren't safe against races themselves! With an immovable std::mutex you know the only things anyone can do to it once it has been constructed and before it has been destroyed is to lock it and unlock it, and those operations are explicitly guaranteed to be thread safe and not introduce data races. This same argument applies to std::atomic<T> objects: unless they could be moved atomically it wouldn't be possible to safely move them, another thread might be trying to call compare_exchange_strongon the object right at the moment it's being moved. So another case where types should not be movable is where they are low-level building blocks of safe concurrent code and must ensure atomicity of all operations on them. If the object value might be moved to a new object at any time you'd need to use an atomic variable to protect every atomic variable so you know if it's safe to use it or it's been moved ... and an atomic variable to protect that atomic variable, and so on...

I think I would generalize to say that when an object is just a pure piece of memory, not a type which acts as a holder for a value or abstraction of a value, it doesn't make sense to move it. Fundamental types such as int can't move: moving them is just a copy. You can't rip the guts out of an int, you can copy its value and then set it to zero, but it's still an int with a value, it's just bytes of memory. But an int is still movable in the language terms because a copy is a valid move operation. For non-copyable types however, if you don't want to or can't move the piece of memory and you also can't copy its value, then it's non-movable. A mutex or an atomic variable is a specific location of memory (treated with special properties) so doesn't make sense to move, and is also not copyable, so it's non-movable.

sbi
  • 204,536
  • 44
  • 236
  • 426
Jonathan Wakely
  • 153,269
  • 21
  • 303
  • 482
  • 18
    +1 a less exotic example of something that can't be moved because it has a special address is a node in a directed graph structure. – Potatoswatter Jan 13 '13 at 15:04
  • 1
    I don't think the implementation has to protect against moving a `std::mutex` while another thread is locking it any more that it has to protect against destroying a `std::mutex` which another thread is trying to lock it. Both represent severe programming errors on the part of the user. A mutex allows you to create synchronized access to other data; it doesn't lift the requirement of proper lifetime management for the mutex object itself. – Ben Voigt Jan 13 '13 at 15:38
  • 3
    If the mutex is non-copyable and non-movable, how can I copy or move an object which contains a mutex? (Like a thread safe class with it's own mutex for synchronization...) – tr3w Jan 13 '13 at 16:22
  • @BenVoigt, right, but for the lowest-level building blocks like mutexes and atomics it's helpful to know that even motivated idiots can't move a mutex you're using. If a mutex hasn't started its destructor you can safely lock or unlock it. (discounting trying to unlock mutexes you don't own and other logic errors) – Jonathan Wakely Jan 13 '13 at 16:38
  • 4
    @tr3w, you can't, unless you create the mutex on the heap and hold it via a unique_ptr or similar – Jonathan Wakely Jan 13 '13 at 16:40
  • 1
    @BenVoigt, also remember that an explicit design requirement of `std::mutex` was that it be eligible for static initialization, so a global `std::mutex` is guaranteed to be usable before dynamic initialization begins, precisely to simplify "proper lifetime management". Knowing it's non-movable also simplifies things. – Jonathan Wakely Jan 13 '13 at 17:26
  • 2
    @tr3w: Wouldn't you just move the entire class *except* the mutex part? – user541686 Jan 13 '13 at 20:32
  • @Mehrdad: You can't *not* move the mutex part. The old object is expiring. The only solution is to forbid move completely. – Ben Voigt Jan 14 '13 at 00:35
  • 3
    @BenVoigt, but the new object will have its own mutex. I think he means having user-defined move operations which move all members except the mutex member. So what if the old object is expiring? Its mutex expires with it. – Jonathan Wakely Jan 14 '13 at 00:59
  • 2
    @BenVoigt: The way I see it, the mutex part is a property of the object's "container" (i.e. storage location), not the object itself. So it should stick around as long as the container is there, not as long as the object is there. – user541686 Jan 14 '13 at 01:12
  • @Mehrdad: depends on the class, I think. Suppose you move the object while holding the lock (I'll ignore the matter of how to avoid locking inversion if you do it while both source and destination are locked). Then as post-condition do you want to hold a lock on the location you moved from that generally has unspecified state, or the moved entity in its new location? That tells you whether to move all except the mutex, vs. hold a mutex by `unique_ptr`. Or you can forbid moving and not have to answer the question :-) – Steve Jessop Jan 14 '13 at 19:20
  • @SteveJessop: *"Suppose you move the object while holding the lock"*... well IMO the problem stems from that premise. It's wrong to begin with, so there's no right solution. On the other hand, there's no reason to forbid moving in *all* cases -- what if you want to move the object when people are *not* holding locks on it (e.g. right when the object is created)? No reason to entirely forbid it just because "there exists a situation in which moving should be forbidden". – user541686 Jan 14 '13 at 19:26
  • @Mehrdad: in which case it makes no external difference which way you implement the lock, it's an implementation detail. Your way might be more performant (less dynamic allocation) whereas Jonathan's way potentially saves having to write any constructors at all. So then I don't think you'd *necessarily* move the whole object except the mutex (as you said to tr3w), but I also don't think you *must* forbid moving (as Ben said to you). You shouldn't write error-prone ctor code just to make the lock "part of the location", though. It's irrelevant what it protects if it's guaranteed unlocked! – Steve Jessop Jan 14 '13 at 19:36
  • @SteveJessop: I'm having trouble following your reasoning, e.g. *"I don't think you'd necessarily move the whole object except the mutex"*... I didn't quite follow why you think that. A mutex's identity is determined by its storage location, not its contents, so it should never be moved. Why do you think it should be moved in some cases? – user541686 Jan 14 '13 at 19:41
  • It suddenly occurs to me, though, maybe your solution is a `MutexForMovableTypes` class, that wraps a mutex and is movable and whose move constructor does nothing to the source. That avoids writing error-prone boilerplate in the move ctor of the class that has one as a data member. – Steve Jessop Jan 14 '13 at 19:42
  • @Mehrdad: I didn't say the mutex should be moved, I said "moving the whole object with the exception of a mutex data member" is not the only valid solution. Another valid solution is Jonathan's - not to have a mutex member at all, instead to hold one by a smart pointer that is moved. Quite aside from anything else it needn't be a `unique_ptr` that you hold it by, your class could allow you to fine-tune your locking granularity if it's a `shared_ptr` :-) – Steve Jessop Jan 14 '13 at 19:43
  • @SteveJessop, I've used a generic type like your `MutexForMovableTypes`, which wraps an object but does nothing when copied/assigned. It allows a class that contains it as a data member to have implicitly-defined copy/move operations but not update that member. Useful when e.g. a class has a GUID or other identity which should be fixed and not transferred (like its address). – Jonathan Wakely Jan 14 '13 at 19:53
  • _(I'll ignore the matter of how to avoid locking inversion if you do it while both source and destination are locked)_ You use `std::lock(this->mx, that.mx)` which is deadlock-free, of course :) – Jonathan Wakely Jan 14 '13 at 19:55
  • @SteveJessop: Oh I see, I didn't mean to claim that's the "only" valid solution for *mutexes in general*, sorry if that confused you. I meant that it's the "only" valid solution **assuming you have a `mutex` object to begin with**. Yes, if you have a `unique_ptr` then the situation changes entirely, but I wasn't talking about that. – user541686 Jan 14 '13 at 19:56
  • 1
    @Mehrdad: in that case I agree with you, *if* the mutex is part of the object (not the value), *then* it can be a data member and unmovable, *but* the surrounding class could be movable anyway, perhaps with the restriction that it's UB to do it with the mutex locked. Ben was basically saying "never mind all that, you have to make it non-movable" when he disagreed with you, but that's only true presuming that the mutex identity is supposed to be part of the value (rather than the object). And *that* all depends on the class, I think, which is where I started ;-) – Steve Jessop Jan 14 '13 at 20:02
  • Challenge: create a standards compliant `int` that is movable. How about an `int` that stores its overflow state (as signed overflow is undefined behavior, we have lots of latitude). When copied, both have the overflow flag -- when moved-from, the overflow flag travels to the new `int`. Assigning to the `int` while it has an overflow flag causes a trap to be set (exception, etc) as you lost track of the overflow. `has_overflow` and `clear_overflow` let you read/remove said overflow. As everything happens after UB, I think this is legal? Second question: is it a `move`able `int`? – Yakk - Adam Nevraumont May 14 '14 at 20:04
57

Short answer: If a type is copyable, it should also be moveable. However, the reverse is not true: some types like std::unique_ptr are moveable yet it doesn't make sense to copy them; these are naturally move-only types.

Slightly longer answer follows...

There are two major kinds of types (among other more special-purpose ones such as traits):

  1. Value-like types, such as int or vector<widget>. These represent values, and should naturally be copyable. In C++11, generally you should think of move as an optimization of copy, and so all copyable types should naturally be moveable... moving is just an efficient way of doing a copy in the often-common case that you don't need the original object any more and are just going to destroy it anyway.

  2. Reference-like types that exist in inheritance hierarchies, such as base classes and classes with virtual or protected member functions. These are normally held by pointer or reference, often a base* or base&, and so do not provide copy construction to avoid slicing; if you do want to get another object just like an existing one, you usually call a virtual function like clone. These do not need move construction or assignment for two reasons: They're not copyable, and they already have an even more efficient natural "move" operation -- you just copy/move the pointer to the object and the object itself doesn't have to move to a new memory location at all.

Most types fall into one of those two categories, but there are other kinds of types too that are also useful, just rarer. In particular here, types that express unique ownership of a resource, such as std::unique_ptr, are naturally move-only types, because they are not value-like (it doesn't make sense to copy them) but you do use them directly (not always by pointer or reference) and so want to move objects of this type around from one place to another.

Herb Sutter
  • 1,858
  • 1
  • 12
  • 13
  • I don't know how valid your claim is... there's a [Dr. Dobbs article](http://www.drdobbs.com/cpp/a-base-class-for-intrusively-reference-c/229218807) for a ref-counted class (perfect example of the inheritance you're talking about) where they *specifically* implement `swap` as a no-op (because that's the correct implementation -- it measures the lifetime of the storage location, which is unaffected by the object's data/movement/etc.). Obviously if you wouldn't want to implement move then you wouldn't want to implement `swap` either, but they do it and it's the correct thing to do. – user541686 Jan 13 '13 at 11:55
  • 61
    Would the [real Herb Sutter](http://stackoverflow.com/users/297582/herb-sutter) please stand up? :) – fredoverflow Jan 13 '13 at 11:58
  • 6
    Yeah, I switched from using one OAuth Google account to another and cant be bothered to look for a way to merge the two logins that gives me here. (Yet another argument against OAuth among much more compelling ones.) I probably won't use the other one again, so this what I'll use for now for the occasional SO post. – Herb Sutter Jan 13 '13 at 12:07
  • 7
    I thought that `std::mutex` was immovable, as POSIX mutexes are used by address. – Puppy Jan 13 '13 at 12:09
  • 9
    @SChepurin: Actually, that is called HerbOverflow, then. – sbi Jan 13 '13 at 12:18
  • 3
    @Herb: I flagged your comment for a moderator to merge your accounts. – sbi Jan 13 '13 at 12:19
  • 2
    As DeadMG says, `std::mutex` is certainly the wrong example. `std::mutex` cannot be moved nor copied. `std::unique_lock` is the movable lockable type. – R. Martinho Fernandes Jan 13 '13 at 13:28
  • 6
    As others have said, `std::mutex` isn't movable, it doesn't _own_ a resource, it _is_ a resource. The native OS mutex type can/should be embedded right in the `std::mutex` object to allow it to be statically initialized via its `constexpr` constructor. No resource allocation involved. `std::thread` would be a better example. – Jonathan Wakely Jan 13 '13 at 14:39
  • 27
    This is getting a lot of upvotes, has noone noticed it says when a type should be move-only, which is not the question? :) – Jonathan Wakely Jan 13 '13 at 15:07
  • Indeed, instead of `std::mutex` the good example would be `std::unique_lock`. – tr3w Jan 13 '13 at 16:14
  • 1
    @JonathanWakely. I did notice, but don't think it warrants a downvote.... For newbies into C++11/move-semantics Herb's answer is a good guideline... – André Jan 13 '13 at 16:38
  • 3
    My downvote stands… there are probably already other questions for this answer. Although, @Mehrdad will certainly select Jonathan's answer and that will go to the top regardless. – Potatoswatter Jan 14 '13 at 01:55
18

Actually when I search around, I found quite some types in C++11 are not movable:

  • all mutex types(recursive_mutex , timed_mutex, recursive_timed_mutex,
  • condition_variable
  • type_info
  • error_category
  • locale::facet
  • random_device
  • seed_seq
  • ios_base
  • basic_istream<charT,traits>::sentry
  • basic_ostream<charT,traits>::sentry
  • all atomic types
  • once_flag

Apparently there is a discussion on Clang: https://groups.google.com/forum/?fromgroups=#!topic/comp.std.c++/pCO1Qqb3Xa4

jamylak
  • 111,593
  • 23
  • 218
  • 220
billz
  • 41,716
  • 7
  • 75
  • 95
  • 1
    ... iterators should not be movable?! What... why? – user541686 Jan 14 '13 at 05:09
  • yeah, I think `iterators / iterator adaptors` should be edited away as C++11 has move_iterator? – billz Jan 14 '13 at 05:23
  • Okay now I'm just confused. Are you talking about iterators that move their *targets*, or about moving the iterators *themselves*? – user541686 Jan 14 '13 at 05:25
  • 1
    I think you are mistaking objects without explicit move operations (or which cannot be moved more efficiently than copied) for non-movable types. But in the end all types that are copyable are also moveable. Especially all iterators are copyable and thus moveable, not just `std::move_iterator` (which has a totally different purpose, anyway). Likewise are [`std::time_point`](http://en.cppreference.com/w/cpp/chrono/time_point/time_point)s and [`std::duration`](http://en.cppreference.com/w/cpp/chrono/duration/duration)s (and maybe others I didn't check/think about more thoroughly). – Christian Rau Jan 14 '13 at 08:42
  • 1
    So is [`std::reference_wrapper`](http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper/reference_wrapper). Ok, the others indeed seem to be non-movable. – Christian Rau Jan 14 '13 at 08:53
  • @ChristianRau thanks for the comment. I'll edit `iterator/iterator adaptors` away from answer. Also I've posted another question, regarding `iterator` movable: http://stackoverflow.com/questions/14314410/why-should-all-iterators-iterator-adaptors-not-movable-in-c11 – billz Jan 14 '13 at 08:58
  • @billz Still not enough unfortunately. Read the comments and follow the links, assuming `cppreference` correctly mirrors the standard there (not that it would make much sense for those types to be non-copyable by simple reasoning). – Christian Rau Jan 14 '13 at 09:04
  • @ChristianRau yup. regarding Potatoswatter's answer on another question, I've removed more. will check others up though. – billz Jan 14 '13 at 10:36
  • 1
    These seem to fall into three categories: 1. low-level concurrency-related types (atomics, mutexes), 2. polymorphic base classes (`ios_base`, `type_info`, `facet`), 3. assorted weird stuff (`sentry`). Probably the only unmovable classes an average programmer will write are in the second category. – Philipp Jan 14 '13 at 22:58
1

Another reason I've found - performance. Say you have a class 'a' which holds a value. You want to output an interface which allows a user to change the value for a limited time (for a scope).

A way to achieve this is by returning a 'scope guard' object from 'a' which sets the value back in its destructor, like so:

class a 
{ 
    int value = 0;

  public:

    struct change_value_guard 
    { 
        friend a;
      private:
        change_value_guard(a& owner, int value) 
            : owner{ owner } 
        { 
            owner.value = value;
        }
        change_value_guard(change_value_guard&&) = delete;
        change_value_guard(const change_value_guard&) = delete;
      public:
        ~change_value_guard()
        {
            owner.value = 0;
        }
      private:
        a& owner;
    };

    change_value_guard changeValue(int newValue)
    { 
        return{ *this, newValue };
    }
};

int main()
{
    a a;
    {
        auto guard = a.changeValue(2);
    }
}

If I made change_value_guard movable, I'd have to add an 'if' to its destructor that would check if the guard has been moved from - that's an extra if, and a performance impact.

Yeah, sure, it can probably be optimized away by any sane optimizer, but still it's nice that the language (this requires C++17 though, to be able to return a non-movable type requires guaranteed copy elision) does not require us to pay that if if we're not going to move the guard anyway other than returning it from the creating function (the dont-pay-for-what-you-dont-use principle).

saarraz1
  • 2,957
  • 6
  • 25
  • 44