>
>
>
V825. Expression is equivalent to movin…


V825. Expression is equivalent to moving one unique pointer to another. Consider using 'std::move' instead.

Анализатор обнаружил фрагмент кода, в котором совместно используются функции 'std::unique_ptr::reset' и 'std::unique_ptr::release'.

Рассмотрим простой пример кода:

void foo()
{
  auto p = std::make_unique<int>(10);
  ....
  std::unique_ptr<int> q;
  q.reset(p.release());
  ....
}

Формально, такой вызов эквивалентен перемещению умного указателя:

void foo()
{
  auto p = std::make_unique<int>(10);
  ....
  auto q = std::move(p);
  ....
}

В данном случае, предложение анализатора заменить цепочку вызовов 'q.reset(p.release())' на 'q = std::move(p) ' улучшит прозрачность кода. Однако, может возникнуть ситуация, когда перемещение умного указателя будет являться обязательным. Например, при использовании пользовательского функционального объекта для освобождения ресурса:

class Foo { .... };

struct deleter
{
  bool use_free;

  template<typename T>
  void operator()(T *p) const noexcept
  {
    if (use_free)
    {
      p->~T();
      std::free(p);
    }
    else
    {
      delete p;
    }    
  }
};

Рассмотрим два небольших примера, первый с перемещением умного указателя с пользовательским функциональным объектом для освобождения ресурса, при помощи паттерна 'reset' - 'release':

void bar1()
{
  std::unique_ptr<Foo, deleter> p { (int*) malloc(sizeof(Foo)),
                                     deleter { true } };
  new (p.get()) Foo { .... };

  std::unique_ptr<Foo, deleter> q;

  q.reset(p.release()); // 1
}

и второй пример, с помощью функции 'std::move':

void bar2()
{
  std::unique_ptr<Foo, deleter> p { (int*) malloc(sizeof(Foo)),
                                    deleter { true } };
  new (p.get()) Foo { .... };

  std::unique_ptr<Foo, deleter> q;

  q = std::move(p);     // 2
}

В втором примере при перемещении указателя 'p' в 'q' функция 'std::move' позволит переместить также и функциональный объект типа 'deleter' для освобождения ресурса. В первом примере цепочка вызовов 'q.reset(p.release())' этого не сделает. Это приведет к тому, что исходный объект типа 'Foo', аллоцированный на куче через вызов 'malloc' и сконструированный оператором 'placement new', будет неверно освобожден путем вызова оператора 'delete'. Такой код неминуемо приведёт к неопределённому поведению программы.