Main Content

MISRA C++:2023 Rule 28.6.2

Forwarding references and std::forward shall be used together

Since R2024b

Description

Rule Definition

Forwarding references and std::forward shall be used together.

Rationale

You typically use a forwarding reference parameter, which is a parameter of type T &&, in function templates to forward the parameter to other functions while preserving the value category, such as lvalue or rvalue, and constness of the parameter.

Forwarding references use complex C++ language features that can be difficult to master. Wrapping a forwarding reference parameter in a call to the function std::forward<T> can make your intent clearer and to avoid unintended code behavior.

Forwarding references use advanced C++ language features such as:

  • Type deduction, to obtain the type of the forwarding reference based on the argument passed to the function.

  • Reference collapsing, to preserve the original value category of the forwarded reference.

Polyspace Implementation

The coding rule checker reports a violation if one of these is true:

  • You forward a forwarding reference parameter without wrapping the parameter in a call to the function std::forward<T>.

  • You wrap a forwarding reference parameter in a call to std::move<T> or you wrap an rvalue reference in a call to std::forward<T>.

  • The function std::forward<T> is instantiated with a type that does not match the type of the forwarded reference parameter.

For example, in this code snippet, the func template function takes a forwarding reference and passes it to the function otherFunc. The first call to otherFunc is noncompliant because it uses std::move<T> instead of std::forward<T>. The second call to otherFunc is noncompliant because std::forward<T> is instantiated with a float, which does not match the type of the forwarded variable, an int.

#include <iostream>
#include <utility>

void otherFunc(int && var)
{
        std::cout << "Forwarded rvalue: " << var << "\n";
}

template <typename T>
void func(T && fwdref)
{
        otherFunc(std::move(fwdref));           // Noncompliant
        otherFunc(std::forward<float>(fwdref)); // Noncompliant
}

void main()
{
        func(42);
}

Polyspace® does not report a violation if no forwarding takes place. For example, in this code snippet, std::forward<T> is not used with forwarding references. Polyspace does not consider this incorrect use of std::forward<T> a violation.

class myClass
{
};

void func(myClass &lval, myClass &&rval)
{
    const myClass &var1 = std::forward<myClass>(lval); // Compliant
    const myClass &var2 = std::forward<myClass>(rval); // Compliant
}

Troubleshooting

If you expect a rule violation but Polyspace does not report it, see Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

#include <iostream>

class myType
{
public:
    myType() = default;
    myType(const myType &) {} // copy constructor
    myType(myType &&) {}      // move constructor
    ~myType() = default;
};

// func overloads
void func(myType &) //first
{
}
void func(myType &&) //second
{
}

template <typename T1, typename T2>
void funcWrapper(T1 &&var1, T2 &var2)
{
    func(var1);                   // Noncompliant
    func(std::forward<T1>(var1)); // Compliant
    func(std::move(var1));        // Noncompliant
    func(std::move(var2));        // Compliant
}

void exampleUsage()
{
    myType d;
    myType rvalueData;

    funcWrapper(d, d);
    funcWrapper(std::move(rvalueData), d);
}

            
         

In this example, template function funcWrapper takes a forwarding reference, var1, and an lvalue reference, var2, as input parameters. When the function exampleUsage calls funcWrapper:

  • The first call to func inside funcWrapper is noncompliant because the forwarding reference var1 is not wrapped in a call to std::forward<T>. If var1r is an rvalue, then passing it to func does not preserver value category because var1 is named inside funcWrapper and is treated as an lvalue.

  • The third call to func is not compliant because it uses std::move<T> to forward a forwarding reference instead of std::forward<T>. If var1 is an lvalue, the passing it to func does not preserver the value category because std::move<T> casts var1 to an rvalue.

Check Information

Group: Algorithms library
Category: Required

Version History

Introduced in R2024b