Вопрос о поведении, которое я наблюдаю при тестировании передачи параметра как lvalue-copy, rvalue-move и передачи по ссылке

#c #c 17 #move

#c #c 17 #переместить

Вопрос:

В приведенном ниже коде я тестирую передачу по lvalue (должен проходить через конструктор копирования), rvalue (должен проходить через конструктор перемещения) и передачу по ссылке (не должен вызывать конструктор) в стеке. Однако при выполнении строки Func(MyType1()) я подумал, что MyType1() — это r-значение, которое должно запускать конструктор перемещения. Да, на выходе показано, что он не запускает ни конструктор копирования, ни конструктор перемещения. Кажется, что он проходит через передачу или передачу по ссылке lvalue или rvalue. Почему это так?

Второй вопрос, вероятно, связан с моим непониманием того, что можно сделать в move. В моем классе объект MyType1 имеет массив символов. В обоих случаях копирования / перемещения, похоже, для массива, я могу выполнять только глубокое копирование; не мелкое копирование путем настройки указателей (моя цель — избежать дорогостоящего глубокого копирования массива элемент за элементом). В идеале, в move ctor я подумал, что мог бы сделать так, чтобы данные нового объекта указывали на существующий массив в стеке, а указатель массива в исходном объекте стека обнулялся. Но, похоже, это возможно только для указателей на объекты кучи. Я что-то упустил?

в dc 0x7fff3727e114

в cc 0x7fff3727e11e

в функции

в de 0x7fff3727e11e

в mc 0x7fff3727e11e

в функции

в de 0x7fff3727e11e

в dc 0x7fff3727e11e

в функции

а а а а а а а а а а а а а

в de 0x7fff3727e11e

в FuncRef

а а а а а а а а а а а а а

в 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 amp;o)
  {
    cout << "in cc " << this << "n ";
    // *data = *o.data; doesn't work, have to deep copy?
  }

  MyType1(MyType1 amp;amp;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 Funcn";
  other.Print();
}

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

Ответ №1:

Это не совсем ответ на ваш вопрос, но (я думаю) затрагивает суть вашего непонимания.

Рассмотрим следующую структуру:

 struct One {
   char data[10];
};
  

Эта структура имеет длину 10 байт. Каждый объект типа One содержит массив из десяти элементов. Не указатель на массив, а фактический массив.

Итак, когда вы говорите, что хотите сделать «мелкую копию», что это значит? Вы не можете «указать массив» на другой массив — это не то, как работают массивы. Если бы у вас был указатель, вы могли бы это сделать, но структура One не хранит указатель, а только массив. (Да, вы можете создать указатель на data , но вы не можете сохранить этот указатель в объекте типа One )

Наличие массива как части One объекта также означает, что хранилище для массива существует только до тех пор, пока объект, частью которого он является. Таким образом, даже если бы вы могли «переназначить» массив, это вызвало бы проблемы, когда исходный (исходный) объект был уничтожен (и хранилище использовалось повторно).

Ответ №2:

Вы задали здесь 2 вопроса:

  1. Func(MyType1()); Почему не вызывался move ctor после ctor по умолчанию? Ответ — «копировать elision»: стандарт C позволяет пропустить копирование или перемещение конструктора и вместо этого использовать исходный объект (начиная с C 17, elision в некоторых случаях является обязательным). Правила меняются между версиями стандарта и являются сложными. Вы никогда не должны зависеть от вызываемого конструктора копирования / перемещения.
  2. Вы правильно понимаете: вы не можете оптимизировать с помощью конструктора перемещения, если вы не используете кучу.