The analyzer detected a situation when move semantics does not work. Such code slows down the performance.
Example:
#include <string> #include <vector> void foo() { std::vector<std::string> fileData; const std::string alias = ....; .... fileData.emplace_back(std::move(alias)); .... }
This code does not work as the developer expects. Move semantics is impossible for const-qualified objects. As a result, the compiler calls a copy constructor for 'std::string' and the expected optimization does not happen.
To fix this code, you can remove the 'const' keyword from the 'alias' local variable:
#include <string> #include <vector> void foo() { std::vector<std::string> fileData; std::string alias = ....; .... fileData.emplace_back(std::move(alias)); .... }
The diagnostic also issues a warning when 'std::move' is used on a function's formal parameter:
#include <string> void foo(std::string); void bar(const std::string &str) { .... foo(std::move(str)); .... }
There's no universal way to fix such code, but the approaches below could help.
Approach 1
Add a function overload that takes an rvalue reference:
#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 .... }
Approach 2
Rewrite the function to make it a function template that takes a forward reference. Limit the template parameter to the required type. Then apply the 'std::forward' function to the template argument:
#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)); .... } // -------------------------------------------------------------------
Approach 3
If the above - or any other - approaches are not applicable, remove the 'std::move' call.
The diagnostic rule also fires when the 'std::move' function's result is passed to a function that takes an lvalue reference to a const. Example:
#include <string> std::string foo(const std::string &str); void bar(std::string str, ....) { .... auto var = foo(std::move(str)); .... }
Although 'std::move' is executed and returns an xvalue object, that object is still copied and not moved. This happens because the function's formal parameter is an lvalue reference to a const. In this case, the result of the 'std::move' call falls within the context where a move constructor call is impossible. However, if you write a new function overload, that takes an rvalue reference, or a function template with a forwarding reference - the compiler will choose that entity and will execute the code as you expect:
#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)); .... }
Now let's examine the case when 'std::move' can be applied to a reference to a const and works correctly:
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; };
The code above is the MoC (Move on Copy) idiom implementation. The copy constructor moves the object. In this case, it is possible because the non-static data member 'obj' has the 'mutable' specifier and tells the compiler explicitly to process this object as a non-const object.