2

I have a general question about move semantics. Yesterday I just played around to get more comfortable with this topic. Here I added copy and move constructor operators, that simply log to the console:

#include <iostream>

class Test {    
public:
  const char* value = "HI";
  Test(){
     std::cout << "Default Constructed\n";
  }
  Test(Test &&source) { 
     std::cout << "Move Constructed\n";
  }
  Test(const Test &source) {
     std::cout << "Copy Constructed\n";
  }
};    

Now when I call

void showMe(Test&& test) {
  std::cout << test.value;
}

int main() {
  Test test1;
  // Test test2{test1};
  showMe(std::move(test1));

  return 0;
}

The console logs out:

> Default Constructed

> HI

Which is clear to me, since we are just moving the ownership to showMe. But why wasn't the Test's move constructor operator be called?

But now, if I change showMe function into

void showMe(Test test) {
  std::cout << test.value;
}

the console shows:

> Default Constructed

> Move Constructed

> HI

So my question is, why was the Move constructor operator not executed in the first case, but in the second? I usually thought in the first case, the Test's move operator should execute. What are the differences between those two cases mentioned?

Chris
  • 75
  • 1
  • 5
  • In the second case how else would you expect the `Test test` object to be constructed? In the first case which object would you expect to be move-constructed? – user17732522 Sep 01 '22 at 08:21
  • 2
    Invoking `std::move` does not "move the ownership" or create objects. The only effect is has is that it forms an expression of rvalue category which can be binded to rvalue reference. – user7860670 Sep 01 '22 at 08:24
  • To be more explicit, all `std::move(test1)` does is `static_cast(test1)`. ("Move" is a confusing name for this operation, but it's not easy to think of a better one.) – molbdnilo Sep 01 '22 at 09:00

3 Answers3

3

But why wasn't the Test's move constructor operator be called?

Because you keep operating on the same object via a reference to an rvalue. No new object is being constructed, hence no constructor is necessary.

You could conceivably keep passing that reference down, just like you can do with a regular reference or a reference to const - the very nature of references is that they're not objects.

In your second example, the parameter to the function is a value, and in order to construct that value, the second constructor needs to fire.

Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
  • Ok I got it. In the first case the function expects / uses a r-value reference. Simple spoken, a object that got already created, just passed down, i.e. a reference to it. In the second case the function expects a value, that has to be constructed. Since we are passing in a r-value (because of std::move) the move constructor gets called, uses the r-value and calls memmove for the class methods creating a new class instance. – Chris Sep 01 '22 at 10:07
1

In the first place you pass a reference. Therefore, no new object needs to be created.

MrTux
  • 32,350
  • 30
  • 109
  • 146
-2

in void showMe(Test test) formal argument expect passed by value so that is the reason Move Constructed\ gets called (Passing by value means that the parameter is copied into the function. That calls the move constructor ) .in second case void showMe(Test&& test) formal argument expect pass by reference hence no new object created , no constructor gets called

  • 1
    Even after a significant edit to clean up the answer, I'm still not sure if it's right or not. Some of it is, but the rest leaves me puzzled. – user4581301 Sep 09 '22 at 20:23