What is the state of the message for the move constructor?

Consider a class:

class foo {
public:
  foo(foo &&rhs) { /* some code */ }
  ~foo() noexcept { /* code that does destruction of owning objects */ }
private:
  /* some non-trivial pointer graph like structure */
};

      

Let's assume:

{
  foo &f = get_from_other_class();
  foo g = std::move(f);
  // some time later f goes our of scope or the owning object is destroyed.
}

      

What post conditions apply to f

post execution std::move(f)

?

Note

I have a suspicion that f

it should still be purely destructible (without destroying ownership of the content g

), but I did not find a relevant quote in the C ++ 11 standard. I am looking at 12.8 copying and moving class objects.

+3


source to share


6 answers


Every object should be destructible, unless you ever allocate it dynamically. After it has been "moved from", i.e. Has rvalue-related meaning (like the first parameter of the move constructor), the object is still required to be valid and destructible, but it can usually be "undefined" and you just don't have to look at the object anymore and either release it from scope, or reassign it.

Some types provide more reliable guarantees, for example std::unique_ptr

, promised that on move-move, it carries the moved object in a state equal to nullptr

.



The general idea of ​​rvalue and rvalue references is that an object where the value of r should not have any aliases, specifically, when an object is bound to an rvalue reference, then there is no other object alias. The standard library makes these assumptions for the rvalue reference interfaces it provides.

+3


source


The postology is that your relocated object is in an undefined but valid state. And that is exactly it: it is unspecified, which means that it can be whoever you choose it (or the library developer) as long as it is valid.



+2


source


What message conditions apply to f after std :: move (f) is executed?

Mostly it can be dictated by the author foo

, but it is also limited by the algorithms (std or not) that set requirements for foo

.

A lot of algorithms will require that the moved state foo

has Destructible

and can be assigned a new value. But it depends on the context in which it will be used foo

.

If foo

used with the std-library component, then the requirements are dictated by Table 20 - MoveConstructible Requirements:

condition

rvs not specified [Note: rv must still meet the requirements of the library component that uses it. The operations listed in these requirements should work as specified, whether rv was moved from or not. - end note]

For example: suppose you are calling std::sort

with vector<foo>

. std::sort

required foo

: Swappable

, MoveConstructible

, Destructible

, MoveAssignable

and LessThanComparable

.

std::sort

puts these claims in foo

regardless of whether it is foo

in a displaced state
.

Strictly speaking, when using c, you std::sort

don't need to have a foo

be LessThanComparable

in a state with a transition. It would be pointless for the constructor to compare the moved object since its value is not specified. However, C ++ 11 and C ++ 14 are currently required LessThanComparable

. The result does not have to be reasonable, but it doesn’t require a crash to execute. A future standard may weaken this requirement, but who knows.

So, the author foo

can specify what operations are allowed for the moved from foo

.

And any algorithm can specify what it requires of the types on which it operates.

At the intersection where the foo

requirements of the algorithm are met, the code runs. The C ++ standard does not address the special moved state.

+2


source


Table 20 (§ 17.6.3.1) defines the MoveConstructible requirements:

+ ------------ + ------------------------------------ ---------------------------- +
| Expression | Post-condition |
+ ------------ + ------------------------------------ ---------------------------- +
| T u = rv; | u is equivalent to the value of rv before the construction |
| T (rv) | T (rv) is equivalent to the value of rv before the construction |
+ ------------ + ------------------------------------ ---------------------------- +
condition

rvs not specified [Note: rv must still meet the requirements for the library component that uses it. the operations listed in these requirements should work as specified, rv was moved from or not. - end note]

The note indicates that @Kerrek's answer is that a library component might have different requirements.

+1


source


The moved object must be ready for the destructor to run.

Per Stroustrup, The C ++ Programming Language, 4th Edition 2014

If x is moved from, x will have "some moved state" ...

"for the most interesting case, containers that have moved state" empty "" ...

I tend to think of this as the affinity of the default initialized variable.

The value of built-in types does not change after being moved.

default moved-from state

is where the default destructor and default copy assignment work correctly.

One definition of this is that if the destructor is running, any resource previously managed by the object will no longer be managed by the object; the destructor will leave it alone or unaware. I suppose this is implementation / user dependent.

In short, you can do whatever you want with x

, and it will be correct as with the new variable.

+1


source


as it is move

, so after the g = std::move(f);

semantic content f

should be invalidated and g

checked. move

only supports one valid copy of the semantic content. The semantics of "invalidate / validate" depends on the class, for example, if f

is a string, it should be 0, if f

is a stream, it should be set to std::thread()

. However, the detailed behavior depends on the class move constructor . Of course it f

is still crumbling.

-1


source







All Articles