0

I have a piece of code to check if move happened.

struct Foo
{
  Foo() = default;
  Foo(Foo&& other) = default;

  double a[1000];
  double b[1000];
};

Foo giveMe()
{
  Foo ret;
  std::cout << &ret << std::endl;

  return ret;
}

int main()
{
  Foo o1 = giveMe();
  std::cout << &o1 << std::endl;

  return 0;
}

Well, it does not. The addresses of ret and o1 are, for instance, as follows:

008F7D78
008FBC08

However, after slight change in move constructor:

struct Foo
{
  Foo() = default;
  Foo(Foo&& other) {}

  double a[1000];
  double b[1000];
};

the addressees are the same:

010FBE28
010FBE28

What is happening here? I assume that this behaviour is somehow related to POD or trivial types but I am not sure how.

tucna
  • 165
  • 2
  • 8
  • 2
    "Moving" means to move resources from one instance to another. Not to move an instance to another memory address. – Lukas-T Aug 10 '21 at 10:19
  • @churill I am aware of it. The address check is there just to see if move or copy happened. – tucna Aug 10 '21 at 10:22
  • It depends on the compiler and compiles flags. – prehistoricpenguin Aug 10 '21 at 10:23
  • You are looking for copy elision. Are you in C++17? – Jarod42 Aug 10 '21 at 10:24
  • 1
    *"after slight change"* first snippet move (so copy for this type) members whereas second snippet doesn't initialize members. – Jarod42 Aug 10 '21 at 10:27
  • I am guessing you use MSVC. GCC and Clang elide "big objects" too, despite the allowance described in the dupe – StoryTeller - Unslander Monica Aug 10 '21 at 10:27
  • I am truly using MSVC. The answer in the linked question, thanks for your time guys! – tucna Aug 10 '21 at 10:32
  • 1
    @tucna I think you have a slight misconception about moving. Imagine `Foo a; Foo b(std::move(a));` this would call the move-ctor but both objects will have distinct and constant addresses. And consider that static arrays can never be moved, but only copied, so your move-ctor does just the same as the copy-ctor. – Lukas-T Aug 10 '21 at 10:32
  • @churill I agree with your statements, however I am not sure if we talk about the same thing. How can you explain the different/same addresses, then? – tucna Aug 10 '21 at 10:39
  • 1
    @tucna Basically the compiler recognizes that `ret` will eventually be copied into `o1` and that `ret` will cease to exist. So the compiler elides the copy by writing directly to the `o1`. In one case it doesn't apply this optimization, that's what should be covered in the dupe. – Lukas-T Aug 11 '21 at 10:00

1 Answers1

0

What is happening here?

Optimisation is happpening here. More precisely: Named Return Value Optimisation (NRVO).

Even though the abstract machine moves in both cases, NRVO optimised (elided) the move away from the second example.

The address check is there just to see if move or copy happened.

The class isn't copyable, so a copy can't have happened. Also, the address check cannot distinguish between copied and moved object. Either would have a different address.

To clarify a possible misconception: "Move" construction creates a new object using the move constructor. That new object typically has a different address than the object that was moved from, just like when copying. The only case where the address of the new object is the same is when the move is optimised away such that the constructor is never actually called (which can also be done with copies).

eerorika
  • 232,697
  • 12
  • 197
  • 326