简体   繁体   中英

Question on behavior I have observing when testing passing parameter as lvalue-copy, rvalue-move and pass by ref

In the below code, I am testing pass by lvalue (should go through copy constructor), rvalue (should go through move constructor) and pass by reference (should invoke no constructor) in stack. However, when executing the line Func(MyType1()) , I thought MyType1() is an r-value which should trigger move constructor. Yes in the output, it shows it trigger neither copy nor move constructor. It seems going through the pass or pass by lvalue or rvalue reference. Why is that?

The second question is probably due to my misunderstanding of that can be done in move. In my class, MyType1 object has a char array. In both copy/move c'tor, it seems for array, I can only do deep copy; not a shallow copy by tweaking pointers (my goal is to avoid expensive deep copy of array element by element). Ideally, in move c'tor, I thought I could make the new object's data points to existing array in the stack while null the array pointer in the source stack object. But it seems that only possible for heap object pointers. Am I missing something?

in dc 0x7fff3727e114

in cc 0x7fff3727e11e

in Func

in de 0x7fff3727e11e

in mc 0x7fff3727e11e

in Func

in de 0x7fff3727e11e

in dc 0x7fff3727e11e

in Func

aaaaaaaaaa

in de 0x7fff3727e11e

in FuncRef

aaaaaaaaaa

in de 0x7fff3727e114

class MyType1
{
public:
  ~MyType1() 
  {
    cout << "in de " << this << "\n";
  }

  MyType1()
  {
    cout << "in dc " << this << "\n";
    for (int i = 0; i < 10; i++)
    {
      data[i] = 'a';
    }
  }

  MyType1(MyType1 &o)
  {
    cout << "in cc " << this << "\n ";
    // *data = *o.data; doesn't work, have to deep copy?
  }

  MyType1(MyType1 &&o)
  {
    cout << "in mc "<< this << "\n ";
    // *data = *o.data; doesn't work, have to deep copy? also can't free source array?
  }

  void Print()
  {
    for (int i = 0; i < 10; i++)
    {
      cout << data[i] << " ";
    }

    cout << "\n";
  }

  char data[10];
};

void Func(MyType1 other)
{
  cout << "in Func\n";
  other.Print();
}

void FuncRef(MyType1& other)
{
  cout << "in FuncRef\n";
  other.Print();
}
    
int main()
{
  MyType1 t;
  Func(t);
  Func(std::move(t));
  Func(MyType1());
  FuncRef(t);
}

This is not quite an answer to your question, but (I think) cuts to the heart of your misunderstanding.

Consider the following struct:

struct One {
   char data[10];
};

That struct is 10 bytes long. Each object of type One contains an array of ten elements. Not a pointer to an array, but an actual array.

So when you say you want to do a "shallow copy", what does that mean? You can't "point the array" at another array - that's not how arrays work. If you had a pointer, you could do that, but the struct One does not store a pointer, just an array. (Yes, you can make a pointer to data , but you can't store that pointer in an object of type One )

Having the array as part of the One object also means that the storage for the array only lives as long as the object that it is part of. So even if you could "re-point" the array, that would cause problems when the original (source) object was destroyed (and the storage reused).

You asked 2 questions here:

  1. Why Func(MyType1()); did not call move ctor after default ctor? The answer is "copy elision": C++ standard allows to skip copy or move constructor and use the original object instead (starting from C++17, elision is mandatory in some cases). The rules change between versions of the standard and are complicated. You should never depend on a copy/move constructor being called.
  2. You understand it right: you cannot optimize using a move constructor if you are not using heap.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM