19

In this question discussed When to make a type non-movable in C++11 and I discovered Scott Meyers had similar question on comp.std.c++, where SG listed below class types are not movable in C++11 libeary.

  • all mutex types(recursive_mutex , timed_mutex, recursive_timed_mutex,
  • condition_variable
  • type_info
  • error_category
  • locale::facet
  • random_device
  • seed_seq
  • reference_wrapper
  • duration
  • time_point
  • - all iterators / iterator adaptors
  • ios_base
  • basic_istream::sentry
  • basic_ostream::sentry
  • all atomic types
  • once_flag

The question is why is all iterators / iterator adaptors not-movable ?

Community
  • 1
  • 1
billz
  • 44,644
  • 9
  • 83
  • 100

4 Answers4

15

That post, from a year before the standard was ratified, is outdated. The poster is Daniel Krügler, an active committee member, and it's a bit of political lobbying:

These aren't moveable and probably some more by accident, because the rules for implicitly generated move operations became clarified just at the Pittsburgh meeting. A general library issue has been opened

http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#1331

to cope for lack of move support in the library. Be sure that you contact you national body representative, because I've heart that this issue is not a sufficient replacement for a national body comment against the FCD.

In other words, all those types being non-movable would be a showstopper bug for the standard, and he wants readers in the Usenet audience to demand that the problem be fixed before the standard becomes official.

The defect has been moved to the "closed" list. The resolution is (link provided for convenience):

Review the library portion of the spec and incorporate the newly added core feature Move Special Member Functions (N3044).

Since N3044 is a hefty bit of material, it's easy to see why it would be essential for such basic functionality to work.

Iterators, and anything else with simple value semantics like std::duration and std::time_point, are certainly movable. As others have mentioned, copyability implies movability, and if it didn't the language would be broken. This post wasn't wrong at the time; rather it's arguing about the brokenness of the unfinished language.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • Now that is a good answer to IMHO actual problem of the question! – Christian Rau Jan 14 '13 at 10:00
  • @ChristianRau I had to fix the URL before reading the Usenet source… possible nobody else actually followed the link – Potatoswatter Jan 14 '13 at 10:02
  • +1 thanks @Potatoswatter I'll try to follow the link if I can, thanks for the guide. – billz Jan 14 '13 at 10:17
  • Can you clarify what "copyability implies movability" means *exactly*? How does it apply to a class that defines copy operations and destructor only? How does it apply to a class that defines copy operations, destructor and deletes move operations? I tend to find the term "movable" unprecise. It's not obvious to me what is meant. Does "movable" just mean that you can construct a T with an rvalue source (possibly resolving to a copy ctor) or does it mean that a move ctor is actually invoked? – sellibitze Jan 14 '13 at 11:42
  • I don't get the "copyability implies movability" thing. What if the copy ctor is public and explicit but the move ctor is `delete`d and implicit? Then `T a = std::move(b)` will fail even though `a` is certainly copyable... – user541686 Jan 14 '13 at 20:11
  • @Mehrdad That's an exception, but it's also (almost) nonsense in terms of copy/move. Moving always can be modeled as copying, then removing the contents of the original object. That situation would only happen if permission doesn't exist to empty the original, suggesting resource (not value) semantics. Under such circumstances it's a stretch to call one object initialized from another a "copy." Alternately, the class could be (ab)using rvalue references for something other than copy/move semantics. – Potatoswatter Jan 15 '13 at 02:34
  • @sellibitze See my above answer to Mehrdad. In Standardese terms, 17.6.3.1 table 21 says that `CopyConstructible` type arguments to standard templates must also satisfy `MoveConstructible`. – Potatoswatter Jan 15 '13 at 02:37
  • The important thing is that everybody knows what definition of "copyable" and "movable" you have in mind when you talk about these things. It would not hurt adding references to the library definitions of MoveConstructible, CopyConstructible, etc to your answer. Of course, if CopyConstructible is defined as refinement of MoveConstructible, the former implies the latter. But other people possibly do understand "movable" as "has a move constructor". – sellibitze Jan 15 '13 at 09:11
10

I take it you include: "classes which implement moving as ordinary copying" in your list of non-moveable types. . Iterators are considered lightweight objects which are cheap to copy. It wouldn't make sense to mandate a move operator for them. E.g. std::vector<T>::iterator essentially is just a wrapped T*, and copying them is just as cheap as moving.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • `std::vector::iterator` need not even be wrapped, which makes the question's assertion dubious. I didn't follow the link to Usenet… – Potatoswatter Jan 14 '13 at 07:46
  • 1
    @Potatoswatter: At least for `T=bool` you need it wrapped. ;-) Anyhow, `list::iterator` is a wrapper for something like a `list_node*`. – sellibitze Jan 14 '13 at 09:17
2

By "not movable" with respect to iterators you probably mean that the class definition does not contain user-declared move operations. But iterators are still copyable. So, a move request still works and falls back on copying. Because of that, there is no reason to provide move operations in situations where the move operations would do exactly the same as the copy operations. For a typical iterator implementation there is nothing to optimize for w.r.t. moving.

sellibitze
  • 27,611
  • 3
  • 75
  • 95
  • For input iterators it would have made more sense to *only* support move and no copy, though. – flodin Oct 23 '14 at 06:50
1

Short answer

because they are copyable.

Long Answer

We have first to clarify what6 "move" actually means. Von Newman machines don't move data: thy just "copy". Data are copyed form a memory location to another. never "moved".

But at higher abstraction level data can be just pointers to other data. When you copy a pointer nullifying the copyed one, the referred data are said to be "moved" from one "owner" to another.

More generally, an operation that copies a value (lake the address contained in a pointer is) and destroys the original one setting it to a recognizable "invalid" is said to be a "move".

In term of C++ whe can distinguish different type of objects:

  1. The ones containing just a simple value.
  2. The ones containing just a simple pointer or reference that "own" what they refer
  3. The ones containing just a simple pointer or reference not "owing" what they refer
  4. The ones containing huge values.
  5. The ones that represent a physical entity or an operating system (more general "hosting platform") entity.

For all of this types, the concept of "copy" and "move" may have different semantic meaning, and for some of then one operation or the other may have mo meaning at all.

Now Consider type 1 objects:

int a=5; c=0;
c = a;
c = std::move(a);

what do you expect the value of a to be after the move? What about c = a+b? Should a and b be "moved" into operator+ ?

Consider now type 2 objects:

std::unique_ptr<int> pa(new int(5)), pb;
pb = std::move(pa);

Here there are two smart pointer (both of them will be destroyed on scope exit) and only one integer. There is an operation (the delete, in this case) that can be done only once, so only one pointer must retain the "ownership" of the integer. Thsi is the case where "copy" is meaningless, and move is the only supported operation

Now consider type 3 objects:

std::list<int> lst = { 1,2,3,4 };
auto i = lst.begin();
auto j = i; 
*j = *i+5;
++i;
*i = *j;

This makes perfectly sense: it just make the list to become { 6,6,3,4 }. Iterators don't own what they refer: there may be many of them all referring the same value. Copy makes sense, but move doesn't: if we move i into j (instad of copy) no *i and ++i wold be anymore possible.

Now consider objects of type 4:

class A
{
   int m[15000000]; //15 million integers
public:
   int& operator[](unsigned x) { return m[x]; }
   const int& operator[](unsigned x) const { return m[x]; }
};

Such a huge beast will be problematic to be allocated on the stack in the most of the systems. It will most likely leave on the heap, and owned / referred by (smart) pointers. Its address will be moved between its pointers, but the object itself will not be movable. It may still be copyable.

There is another subtle case: when A is itself a pointer to the dynamically allocated huge array: This is the same as std::vector: It is movable since it is itself a "smart pointer" who owns the dynamically allocated data, but may be also copyable, since there may be the case you need a new different copy of the owned data.

Now cosider type 5:

class window
{
private:
   HWND handle;
public:
   window() :handle(CreateWindow(....)) 
   { .... }
   ~window() { DestroyWindow(handle); }
};

Here an instance of window represents a window existing on screen. What does it mean "copy" or "move"?

This is most likely the case for mutex, condition_variable, etc., where both copy and move are disabled.

Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
  • 2
    If you are writing a longer answer, you can write, post, delete, undelete to prevent others from seeing your unfinished post. – RedX Jan 14 '13 at 09:48
  • @Emilio This guy was me and merely downvoted *because* you posted an unfinished answer (thus I could ask you the same question ;)), which is behaviour that should be discouraged (and your *"Short Answer"* wasn't that clear or explaining, it would have maybe fit as a comment). Still I saw no reason to comment my downvote, since the low quality of the *"answer"* was pretty obvious and the downvote was very likely to vanish in the near future, anyway (which it already has). – Christian Rau Jan 14 '13 at 09:54
  • @ChristianRau: you are the sole responsible of your opinions as well I am the only one of mine. The only difference is that I wrote after a fact. Now you speak about "low quality and other blah blah blah" but this opinions have no value after you already played your game. IMHO you are just saying what you have to say to justify your prejudice. Personally I find Redex comment much more constructive, but -again- that's just my opinion – Emilio Garavaglia Jan 14 '13 at 13:02
  • @EmilioGaravaglia *"you are the sole responsible of your opinions as well I am the only one of mine"* - Nailed it. So it is my decision if I downvote answers that I regard poor quality and it is my decision if I don't see the neccessity of a comment explaining the downvote, when at the point in time you are ready to read and react to the comment this downvote will already be on its way to vanish anyway (and if it wasn't its reason would have changed and a different comment to be written based on the actual content of the now finished answer). I don't see any reason to downvote anymore, anyway. – Christian Rau Jan 14 '13 at 13:08