Анализатор обнаружил фрагмент кода, в котором совместно используются функции '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'. Такой код неминуемо приведёт к неопределённому поведению программы.