Main Content

Incorrect value forwarding

Forwarded object might be modified unexpectedly

Since R2020b

Description

This defect occurs when:

  • You use std::move to forward a forwarding reference to a function, including objects of type auto&& or T&&.

  • You use std::forward to forward an rvalue reference to a function.

Consider the following code. In the function template callMethod, the parameter param1 is a forwarding reference and the parameter param2 is an rvalue reference. When forwarding these parameters to the function foo(), the code incorrectly uses std::move on the forwarding reference param1 and std::forward on the rvalue reference param2.

#include<string>
#include<iostream>

template <class T> class  bar{};

template<typename T>
void foo(std::string&& s, bar<T>&&){
	//...
} 

template <typename T>
void callMethod(T&& param1, bar<T>&& param2) {
	
	foo(std::move(param1),std::forward<bar<T>&&>(param2));
}
Forwarding references are declared as T&& or auto&&, and rvalue references are declared as &&. Confusing forwarding references with rvalue references results in incorrect value forwarding.

Polyspace® does not report this defect if std::move or std::forward does not forward a reference to a function. For instance, this code does not forward the rvalue reference b1 and the forwarding reference b2 to another function. Using std::move with b2 or using std::forward with b1 does not cause a defect.

template <typename T1, typename T2>
void func(T1& b1, T2&& b2)
{
    const T1& b10 = std::forward<B>(b1);
    const T2& b20 = std::forward<B>(b2);
    const T1& b11 = std::move(b1);
    const T2& b21 = std::move(b2);
}

Risk

Using std::move with forwarding references can result in an unexpected modification of an lvalue. Using std::forward with rvalue references is error-prone and can increase the complexity of your code.

Fix

  • If you forward an rvalue reference to a function, use std::move to cast the reference to an rvalue.

  • If you forward a forwarding or universal reference to a function, use std::forward to cast the reference to an rvalue only if the object is bound to an rvalue.

Examples

expand all

#include <cstdint>
#include <string>
#include <utility>


class A
{
public:
    explicit A(std::string&& s)
        : str(std::move(s)) 
    {
    }

private:
    std::string str;
};

template <typename ...T>
void f1(T...t);



template <typename T1, typename T2>
void func(T1&& t1, T2& t2)
{
    f1(std::move(t1));            
    f1(std::forward<T1>(t1));//Suggested fix     

    f1(std::forward<T2>(t2));     
    f1(std::move(t2));//Suggested fix            
}

void func_auto(A& var)
{
    auto&& var1 = var;
    f1(std::move(var1));                    
    f1(std::forward<decltype(var1)>(var1));//Suggested fix 
}

void main()
{
    int32_t i;
    func(0, i);
}

In this example, the template function func forwards parameters t1 and t2 to function f1.

  • Because the rvalue reference t2 is forwarded by calling std::forward, Polyspace reports a defect.

  • Because the forwarding reference t1 is forwarded by calling std::move, Polyspace reports a defect. If t1 is initialized with an lvalue, the move might result in an unexpected modification of the parameter.

  • Similarly, Polyspace reports a defect on the use of std::move in func_auto because objects of type auto&& are considered as forwarding references.

For each defect, the subsequent lines of code shows the suggested fix.

Result Information

Group: Programming
Language: C++
Default: On for handwritten code, off for generated code
Command-Line Syntax: INCORRECT_VALUE_FORWARDING
Impact: High

Version History

Introduced in R2020b