1

I have read several answers and and many articles about move semantic so it is simply a static cast to rvalue reference, this question is about its influence on the stack, so is the stack left fragmanted after move? or is it rearranged somehow? as the stack moved objects are not subject to stack unwinding. The following snippet runs fine:

#include <memory>
#include <cassert>

struct A {
    int x = 0;
};

struct B {
    A a;
};

void SetB(B& b) {
    A a1{6}; //pushing into the stack.
    A a2{7}; //pushing into the stack.
    b.a = std::move(a2);
}

int main()
{
    B b;
    SetB(b);
    assert( b.a.x == 7);
}

a2 is defined in SetB stack frame but still available after SetB is finished and the contorl back to main. However; a1 is not despite it is closer to main stack frame?

muaz
  • 550
  • 9
  • 15
  • 8
    "_as the stack moved objects are not subject to stack unwinding_": This premise is wrong. Moved-from objects are still normal objects, just in a moved-from state. They still have their destructor called as normal when their scope is left, not earlier. – user17732522 Jun 28 '22 at 22:45
  • I have no idea how you get the last two sentences. When `SetB` returns both `a1` and `a2` are destroyed. Local variables never live longer than the scope which they inhibit. – user17732522 Jun 28 '22 at 22:49
  • Also moving a trivial class such as `A` is exactly the same as copying it. You need to provide a specialized move constructor or assignment operator that makes use of the permission to move somehow e.g. by reusing dynamic allocations. Otherwise there is no difference. `std::move` just makes an xvalue out of a lvalue so that a rvalue reference can bind to it and a rvalue reference is just usually a convention to say "I don't need the state of the referenced object anymore, do with it whatever you want as long as you leave it in a destructible state". – user17732522 Jun 28 '22 at 22:52
  • Note that moving generally means ownership of a resource is transferred to a different object. The pointer, handle, or whatever that referenced that resource is still there, it just doesn't refer to the resource any more. Using `std::vector` as an example, the `sizeof` the `vector` doesn't change after it's moved, it's handed its buffer to another `vector` and nulled all of its pointers to the buffer. – user4581301 Jun 28 '22 at 22:56
  • @user17732522 correct this "as the stack moved objects are not subject to stack unwinding" is wrong, the dtor of a2 is called. – muaz Jun 28 '22 at 23:19
  • *"so is the stack left fragmanted after move?"* -- why would it be? As you wrote: *"[`move`] is simply a static cast to rvalue reference"*. It's simply a cast, similar to the more complex casting of a `float` to an `int`. How do you see a cast leaving the stack fragmented? *(The point of the articles you read was probably that one should disregard the name "move"; that one should **not** infer that `move` causes objects to literally move around in memory leaving holes behind.)* – JaMiT Jun 29 '22 at 02:27
  • @JaMiT this is exactly the question, if the moved object is not really moved, and it is defined in the callee stack frame, how its value still there after that frame is unwinded, people said only the value is assigned to the caller object but still that value is in the callee stack? – muaz Jun 29 '22 at 06:27
  • @user17732522 "Also moving a trivial class such as A is exactly the same as copying it" if this is true then this resolves all of my concerns, i think if you put it as an answer i will accept it in case you provide some reference. – muaz Jun 29 '22 at 06:53
  • 1
    @muaz *"if the moved object is not really moved,"* -- so far, so good -- *"how its value still there after that frame is unwinded"* -- you've made a key semantic jump here, from the **object** to the **value**. The moved **object** is not really moved, but its **value** may be moved (or copied, which is actually a special case of moved, as counter-intuitive as that may seem). Have you seen [A: What is move semantics?](https://stackoverflow.com/a/18300974/)? – JaMiT Jun 29 '22 at 07:17
  • 1
    Hmm... I wrote up something along these lines at one time. Does [A: Implicit move vs copy operations and containment](https://stackoverflow.com/a/60087494/) help you understand? – JaMiT Jun 29 '22 at 07:23
  • You should not write `void SetB(B& b)` but rather `B SetB()` because in the later case you get copy elision and B does not have to be constructed before the call. Since you want move semantic I assume constructing the real `B` even in a dummy state might be expensive and even move assignment costs time. `B b = SetB()` would avoid the move assignment all together. – Goswin von Brederlow Jun 29 '22 at 08:46
  • @GoswinvonBrederlow I disagree, move for stack allocated variables is just a copy, in your example you rely on RVO, but who knows if that thing is reliable. Furthermore, b might be already there and not a dummy one, I just wanted to make example. – muaz Jun 30 '22 at 06:30
  • @muaz RVO is mandatory since c++17. The one that isn't guaranteed is NRVO. And if `b` is already there then both styles are equivalent so you don't loose anything. – Goswin von Brederlow Jun 30 '22 at 08:24

2 Answers2

6

There's no 'stack' in the C++ standard, it's an implementation detail. So a 'generic' answer is difficult as an implementation can have any kind of effect on the given architecture's stack.

That said, the std::move() does not remove the argument from the stack, it remains in a valid-but-undefined state. For your own classes, you might even implement it. Therefore, it's not possible to 'remove' the variable from anywhere - unless, of course, in the optimization phase of the compiler, if it's not accessed anymore.

a2 is not available after SetB is finished. The value is moved to b.a, therefore, b.a is expected to contain the value that a2 has contained before, but it's not the same variable (nor the same memory address).

lorro
  • 10,687
  • 23
  • 36
  • how `b.a` is expected to contain `a2` value, is that value get copied from `SetB` stack frame into `main` one? so std::move is not that cheap operation? – muaz Jun 29 '22 at 06:41
  • `std::move` actually does absolutely nothing. It just affects the type the compiler sees during compilation. It's the move assignment that does some work. – Goswin von Brederlow Jun 29 '22 at 08:42
  • @muaz Purely speculatively, you might come from a language background (maybe PHP, Java) or just a mindset where assignment to variables works differently. In some languages, writing `7` creates a 'number object' and variables only refer to it. This is not the case with c++ - in c++, your variables have address and the value of the variable (that is stored on the given address) is updated, not the variable's 'target' is updated. That schematic exists, but it's pointers (and is unnecessarily slower in this case). – lorro Jun 30 '22 at 13:37
0

With this answer I want to wrap up all infos I got, which really answer my question:

  • For all variables allocated in the stack default move does normal copy (very expensive in case those variables are big) this is something I tested after answers from JaMit, user17732522 and lorro. Originally the question was because I though the compiler will add some measures to manage the stack without copying everything, but it looks I have over-estimated move (no stack fragmentation nor rearrangement, only copy). So in the example above if A is defined like:
struct A {
    int x[1000] = {};
};

moving A results in copying the whole array as is.

  • moving heap resources all depends on the programmer, where move will cast into rvalue reference and once assigned or called inside ctor it will call move assignment or move constructor, and if those are the default ones, it makes shallow copy of pointers which might lead to Segmentation Fault due to dangling pointers.
muaz
  • 550
  • 9
  • 15