ПроблемаЭта проблема возникает, когда значение исходного объекта считывается после перемещения его содержимого в целевой объект путем вызова std::move
функция явным образом. Polyspace® не помечает доступ к значению перемещенного объекта, если:
Исходным объектом явной операции перемещения являются следующие типы:
std::unique_ptr
std::shared_ptr
std::weak_ptr
std::basic_ios
std::basic_filebuf
std::thread
std::unique_lock
std::shared_lock
std::promise
std::future
std::shared_future
std::packaged_task
Эти объекты не остаются в неопределенном состоянии после явного перемещения их состояния.
Операция перемещения выполняется неявно. Для образца - функция std::remove
может получить доступ к состоянию исходного объекта после неявной операции перемещения. Polyspace не помечает его. Лучшая практика состоит в том, чтобы избегать таких операций и использовать более безопасные альтернативы, которые предотвращают случайный доступ, такие как std::erase
.
Исходный объект является встроенным базовым типом, таким как: int
, enum
, float
, double
, указатель, std::intptr_t
, std::nullptr_t
.
РискПоскольку состояние исходного объекта обычно не задано после операции перемещения, небезопасно выполнять операции, которые полагаются на состояние исходного объекта после операции перемещения. Доступ к состоянию исходного объекта после операции перемещения может привести к нарушению целостности данных, неожиданному значению или незаконному удалению указателя.
ЗафиксироватьИзбегайте операций, которые могут считывать исходный объект после перемещения его содержимого.
Пример - Чтение значения исходного объекта после вызова std::move
#include<string>
#include<iostream>
void F1()
{
std::string s1{"string"};
std::string s2{std::move(s1)};
// ...
std::cout
<< // Noncompliant
s1
<< "\n";
// value after move operation
}
void g(std::string v)
{
std::cout << v << std::endl;
}
void F3()
{
std::string s;
for (unsigned i = 0; i < 10; ++i) {
s.append(1, static_cast<char>('0' + i)); //Noncompliant
g(std::move(s));
}
}
В функциональном F1
, строка s1
явно перемещается в s2
по вызову std::move
. После операции перемещения функция пытается считать s1
. Polyspace помечает эту попытку чтения исходного объекта после явного перемещения.
В функциональном F3
, строка s
явно перемещается, а затем считывается std::string::append
функция. Polyspace помечает эту попытку чтения исходного объекта после явного перемещения.
Коррекция - Чтение значений исходных объектов в заданном состоянии#include<string>
#include<iostream>
void F2()
{
std::unique_ptr<std::int32_t> ptr1 = std::make_unique<std::int32_t>(0);
std::unique_ptr<std::int32_t> ptr2{std::move(ptr1)};
std::cout << ptr1.get() << std::endl; // Compliant by exception
}
void g(std::string v)
{
std::cout << v << std::endl;
}
void F4()
{
for (unsigned i = 0; i < 10; ++i) {
std::string s(1, static_cast<char>('0' + i)); // Compliant
g(std::move(s));
}
}
В функциональном F2
, уникальный указатель ptr1
явно перемещается в ptr2
. Потому что состояние std::unique_ptr
остается в заданном состоянии после перемещения, считывая исходный уникальный указатель после того, как явное перемещение соответствует.
В функциональном F4
, строка s
явно перемещается. В каждой итерации цикла s
инициируется к конкретному содержимому перед срабатыванием операции перемещения. В результате состояние s
задается перед доступом к объекту. Этот метод доступа к исходному объекту после операции перемещения соответствует этому правилу.