Nous utilisons des cookies pour améliorer votre expérience de navigation. En savoir plus
Accepter
to the top
close form

Remplissez le formulaire ci‑dessous en 2 étapes simples :

Vos coordonnées :

Étape 1
Félicitations ! Voici votre code promo !

Type de licence souhaité :

Étape 2
Team license
Enterprise licence
** En cliquant sur ce bouton, vous déclarez accepter notre politique de confidentialité
close form
Demandez des tarifs
Nouvelle licence
Renouvellement de licence
--Sélectionnez la devise--
USD
EUR
* En cliquant sur ce bouton, vous déclarez accepter notre politique de confidentialité

close form
La licence PVS‑Studio gratuit pour les spécialistes Microsoft MVP
close form
Pour obtenir la licence de votre projet open source, s’il vous plait rempliez ce formulaire
* En cliquant sur ce bouton, vous déclarez accepter notre politique de confidentialité

close form
I am interested to try it on the platforms:
* En cliquant sur ce bouton, vous déclarez accepter notre politique de confidentialité

close form
check circle
Votre message a été envoyé.

Nous vous répondrons à


Si vous n'avez toujours pas reçu de réponse, vérifiez votre dossier
Spam/Junk et cliquez sur le bouton "Not Spam".
De cette façon, vous ne manquerez la réponse de notre équipe.

>
>
>
V833. Using 'std::move' function's with…
menu mobile close menu
Analyzer diagnostics
General Analysis (C++)
General Analysis (C#)
General Analysis (Java)
Micro-Optimizations (C++)
Diagnosis of 64-bit errors (Viva64, C++)
Customer specific requests (C++)
MISRA errors
AUTOSAR errors
OWASP errors (C#)
Problems related to code analyzer
Additional information
toggle menu Contents

V833. Using 'std::move' function's with const object disables move semantics.

01 Déc 2021

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

  • The 'std::move' function may have received an lvalue reference to a const object as an argument.
  • 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:

#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.