Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.std.c++ > #526

Re: Moving cv-qualified objects

From Daniel Krügler <daniel.kruegler@googlemail.com>
Newsgroups comp.std.c++
Subject Re: Moving cv-qualified objects
Date 2012-06-04 17:13 -0700
Organization A noiseless patient Spider
Message-ID <jqhkbc$hae$1@dont-email.me> (permalink)
References <CAP_A5ohPu3bxuVfw_HdYkWJ1XLVDrCLjmMu_XptQLx2LUG+4sg@mail.gmail.com>

Show all headers | View raw


On 2012-06-04 02:28, Robert Powell wrote:
>
> I find it a little surprising that 'std::move' doesn't strip away
> cv-ness. Cv-ness only matters (other than minor technicalities) during
> the lifetime of an object: both the constructor and the destructor
> operate on a cv-less object. As moving is, logically, somewhat of an
> early destruction, It seems only reasonable to expect that once an
> object is moved, it's cv-ness no longer makes a difference.


Well "somewhat of an early destruction" is only a higher-level
description, it does not satisfy the constraints of the core language,
which still do not allow to modify a const object - whether you move
it or not. There is no requirement that a moved-from object is no
longer usable. But you as a user can impose such a constraints on
*your* types. E.g. for library-provided types a moved-from object can
be queried by all functions that do not have preconditions. E.g. you
may call the empty() or size() function from any moved-from
std::vector<T> instance.

Further, your description is not really true. Are you aware of a
compiler that accepts the following code:

#include <string>

struct X {
 const std::string cs;
 X() : cs("Hi") {}
 ~X() { cs[0] = 'F'; }
};

?

There is no modification of const members allowed in the destructor of
such a class.

> Why should
> I care if someone steals resources from a const object once it has
> been moved?


It may care, if the move-source was indeed a const object: Moving
objects does intentionally not break const-correctness. If you think
that is OK, you have to apply the const_cast explicitly.

> Here's an example where it bites:
>
> #include<iostream>
> #include<utility>
>
> struct movable {
>      movable() {}
>      movable(const movable&) {std::cout<<   "copy"<<   std::endl;}
>      movable(movable&&) {std::cout<<   "move"<<   std::endl;}
> };
>
> struct naive {
>      movable m;
>      const movable cm;
>
>      naive() {}
>      naive(naive&&   other):
>          m(std::move(other.m)),
>          cm(std::move(other.cm)) // Oops. Calls the copy-ctor.
>      {}
> };
>
> int main()
> {
>      naive a;
>      naive b = std::move(a);
> }
>
> When moving 'a' to 'b', 'a.m' is being moved while 'a.cm' is being
> copied  I see no reason why this is desireable?


I think it is very desirable: I would strongly assume a compiler
error, if the compiler would perform an implicit const-cast on member
cm in the move constructor. If you expect naive to be moveable, you
should not mark potentially moveable members that const members.

> As for an implicit move-ctor (assuming 'naive' didn't explicitly
> define one), it gets trickier.
> Looking at 12.8 "Copying and moving class objects" (I'm reading off
> N3242), this clause uses the term "to move an object" freely, but I
> couldn't find where, if any, "moving an object" is formally defined.

>
>
> This makes it hard to reason about, so I'm assuming that, for the sake
> of that clause, "to move an object" means to direct-initialize some
> other object by an expression that is semantically equivalent to
> 'std::move(object)', AND having this get dispatched to a
> move-constructor (i.e. and not a copy-constructor). With this reading,
> the implicit move-ctor of 'naive' is implicitly deleted, because 'cm'
> is not movable (the copy-ctor gets called), and it's copy-ctor is
> non-trivial.


Even though the term move or copy are defined, the core language
defines the definition of the implicitly generated copy constructor or
move constructor (similarly the corresponding assignment operators).
See 12.8 p15 in N3376.

> So, that's even wrose than before. Now both 'm' and 'cm'
> get copied!


Why is that worse? This is what I would expect to happen.

> I only have access to one compiler that implements implicit move-ctors
> (GCC 4.6.1), and this is indeed what happens. If the copy-ctor of
> 'movable' is defaulted, then 'm' gets moved, and 'cm' doesn't.


Correct, otherwise the compiler would break const-correctness. If you
think that this would be OK (But I can only recommend to stay away
from such an assumption without marking all non-static members as
mutable), you have to apply the const-cast by your-self.
Move-semantics was not invented to break const-correctness, it was
invented to allow implicit data transfer from non-const rvalues. This
also has the effect that you better should not apply const-qualifiers
to return types as in

const std::string get_my_string();

because a function call of get_my_string won't allow to move the
returned object.

> I tried to think of a possible rationale for this. The only thing that
> occured to me was that, maybe, it's a better-safe-than-sorry measure?


It was very important to realize that move-semantics does not conflict
with the existing language-safety, yes.

> If a member is 'const', then the destructor might expect it to
> maintain it's original value, in which case moving it is unsafe. But
> this isn't very convincing. If the user defines a destructor, then no
> implicit move-ctor is generated, so, moving has to be done explicitly.
> In this case, if something shouldn't be moved, then the user just
> shouldn't move it. I don't see how const-ness makes it much different
> from anything else.


"Moveing" is a mutable operation, it is not a destructible operation,
otherwise it would be ruled by the language rules of destroyed
objects. It is important to realize that moveing an object does not
end its life-cycle. Well, you *can* define moving your own objects
that way but I expect that such objects are not useable for most
generic code.

> Is this behavior deliberate, or has this been overlooked?


This was not overlooked.

> (Btw, what's with the rule that an implicit move-ctor is deleted if
> one of the members/bases is non-movable and has a non-trivial copy
> (12.8.12)? If some members are movable and others aren't, wouldn't the
> most obvious move-ctor move what it can, and copy the rest? Why should
> one pay for a full copy?)


Yes, this rule is a defect. There is an issue that attempts to solve
this problem, see

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1402

The current P/R could be further improved and I hope it will be so.

HTH & Greetings from Bremen,

Daniel Krügler



--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]

Back to comp.std.c++ | Previous | NextPrevious in thread | Next in thread | Find similar


Thread

Moving cv-qualified objects Robert Powell<robertpwell@gmail.com> - 2012-06-03 17:28 -0700
  Re: Moving cv-qualified objects Jason McKesson <jmckesson@gmail.com> - 2012-06-04 17:11 -0700
  Re: Moving cv-qualified objects Daniel Krügler <daniel.kruegler@googlemail.com> - 2012-06-04 17:13 -0700
  Re: Moving cv-qualified objects robertpwell@gmail.com - 2012-06-04 19:11 -0600

csiph-web