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