Анализатор обнаружил ситуацию, когда семантика перемещения не сработает, что приведёт к замедлению производительности.
Рассмотрим пример:
#include <string> #include <vector> void foo() { std::vector<std::string> fileData; const std::string alias = ....; .... fileData.emplace_back(std::move(alias)); .... }
Данный фрагмент кода cработает не так, как ожидает программист. Семантика перемещения невозможна для константных объектов. В результате компилятор выберет конструктор копирования для 'std::string' и желаемая оптимизация не произойдёт.
В данном случае код можно поправить, просто убрав константность с локальной переменной:
#include <string> #include <vector> void foo() { std::vector<std::string> fileData; std::string alias = ....; .... fileData.emplace_back(std::move(alias)); .... }
Диагностика выдает срабатывания также и для случаев, когда 'std::move' применяется для формального параметра функции:
#include <string> void foo(std::string); void bar(const std::string &str) { .... foo(std::move(str)); .... }
Дать универсальную рекомендацию по исправлению такого кода сложно, но можно применить следующие подходы.
Первый вариант
Можно дописать перегрузку функции, принимающую rvalue-ссылку:
#include <string> void foo(std::string); void bar(const std::string &str) { .... foo(str); // copy here .... } void bar(std::string &&str) // new overload { .... foo(std::move(str)); // move here .... }
Второй вариант
Можно переписать функцию в виде шаблона функции, принимающей forward-ссылку. При этом необходимо ограничить шаблонный параметр нужным типом. Затем применить на шаблонном аргументе функцию 'std::forward':
#include <string> #include <type_traits> // until C++20 #include <concepts> // since C++20 void foo(std::string); // ------------ Constraint via custom trait (since C++11) ------------ template <typename T> struct is_std_string : std::bool_constant<std::is_same<std::decay_t<T>, std::string>::value> {}; template <typename T, std::enable_if_t<is_std_string<T>::value, int> = 0> void bar(T &&str) { .... foo(std::forward<T>(str)); .... } // ------------------------------------------------------------------- // ------------ Constraint via custom trait (since C++14) ------------ template <typename T> static constexpr bool is_std_string_v = std::is_same<std::decay_t<T>, std::string>::value; template <typename T, std::enable_if_t<is_std_string_v<T>, int> = 0> void bar(T &&str) { .... foo(std::forward<T>(str)); .... } // ------------------------------------------------------------------- // ------------------ Constraint via C++20 concept ------------------- template <typename T> void bar(T &&str) requires std::same_as<std::remove_cvref_t<T>, std::string> { .... foo(std::forward<T>(str)); .... } // -------------------------------------------------------------------
Третий вариант
Если ранее описанные или другие приёмы невозможны, то следует убрать вызов 'std::move'. Диагностическое правило также сработает в случаях, когда результат функции 'std::move' передаётся в функцию, принимающую lvalue-ссылку на константу. Рассмотрим пример:
#include <string> std::string foo(const std::string &str); void bar(std::string str, ....) { .... auto var = foo(std::move(str)); .... }
Хоть 'std::move' отработает и вернёт нам xvalue-объект, он всё равно будет скопирован, поскольку формальный параметр функции — lvalue-ссылка на константу. В данном случае результат вызова 'std::move' будет находиться в контексте, в котором вызов конструктора перемещения невозможен. Однако, если дописать перегрузку функции, принимающую rvalue-ссылку, или шаблон функции с forwarding-ссылкой, компилятор выберет её, и код отработает ожидаемым образом:
#include <string> std::string foo(const std::string &str); std::string foo(std::string &&str); void bar(std::string str, ....) { .... auto var = foo(std::move(str)); .... }
Теперь давайте рассмотрим случай, когда 'std::move' на ссылку на константу сработает:
template <typename T> struct MoC { MoC(T&& rhs) : obj (std::move(rhs)) {} MoC(const MoC& other) : obj (std::move(other.obj)) {} T& get() { return obj; } mutable T obj; };
Здесь представлена реализация идиомы MoC (Move on Copy). В конструкторе копирования выполняется перемещение. В данном случае это возможно потому, что нестатическое поле 'obj' имеет спецификатор 'mutable', и это явно говорит компилятору работать с ним не как с константным объектом.