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.
source to share
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.
source to share
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:
conditionrvs 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.
source to share
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 | + ------------ + ------------------------------------ ---------------------------- +
conditionrvs 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.
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.
source to share
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.
source to share