﻿# V833\. Using 'std::move' function with const object disables move semantics\.

The analyzer detected a situation when move semantics does not work\. Such code slows down the performance\.

1. The '[std::move](https://pvs-studio.com/en/blog/terms/6518/)' function may have received an [lvalue reference](https://pvs-studio.com/en/blog/terms/6517/) to a const object as an argument\.
1. The 'std::move' function's result may have been passed to a function that takes an lvalue reference to a const as a parameter\.

Example:

```cpp
#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:

```cpp
#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:

```cpp
#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:

```cpp
#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:

```cpp
#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:

```cpp
#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:

```cpp
#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:

```cpp
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\.