17

In the code below, why should I use std::forward when passing around arguments?

class Test {
  public:
  Test() {
      std::cout << "ctor" << std::endl;
  }
  Test(const Test&) {
      std::cout << "copy ctor" << std::endl;
  }
  Test(const Test&&) {
      std::cout << "move ctor" << std::endl;
  }
};

template<typename Arg>
void pass(Arg&& arg) {
    // use arg..
    return; 
}

template<typename Arg, typename ...Args>
void pass(Arg&& arg, Args&&... args)
{
    // use arg...
    return pass(args...); // why should I use std::forward<Arg>(args)... ?
}

int main(int argc, char** argv)
{
    pass(std::move<Test>(Test()));

    return 0;
}

The code with or without std::forward doesn't show any copy/move around.

Niall
  • 30,036
  • 10
  • 99
  • 142
Dean
  • 6,610
  • 6
  • 40
  • 90

1 Answers1

21

There are a number of good posts on what std::forward does and how it works (such as here and here).

In a nutshell, it preserves the value category of its argument. Perfect forwarding is there to ensure that the argument provided to a function is forwarded to another function (or used within the function) with the same value category (basically r-value vs. l-value) as originally provided. It is typically used with template functions where reference collapsing may have taken place (involving universal/forwarding references).

Consider the code sample below. Removing the std::forward would print out requires lvalue and adding the std::forward prints out requires rvalue. The func is overloaded based on whether it is an rvalue or an lvalue. Calling it without the std::forward calls the incorrect overload. The std::forward is required in this case as pass is called with an rvalue.

#include <utility>
#include <iostream>
class Test {
  public:
  Test() {
      std::cout << "ctor" << std::endl;
  }
  Test(const Test&) {
      std::cout << "copy ctor" << std::endl;
  }
  Test(Test&&) {
      std::cout << "move ctor" << std::endl;
  }
};

void func(Test const&)
{
    std::cout << "requires lvalue" << std::endl;
}

void func(Test&&)
{
    std::cout << "requires rvalue" << std::endl;
}

template<typename Arg>
void pass(Arg&& arg) {
    // use arg here
    func(std::forward<Arg>(arg));
    return; 
}

template<typename Arg, typename ...Args>
void pass(Arg&& arg, Args&&... args)
{
    // use arg here
    return pass(std::forward<Args>(args)...);
}

int main(int, char**)
{
    pass(std::move<Test>(Test()));
    return 0;
}
compor
  • 2,239
  • 1
  • 19
  • 29
Niall
  • 30,036
  • 10
  • 99
  • 142
  • Why would it be incorrect to call the Test const& version? I mean: what's the price in that? – Dean Oct 24 '14 at 15:23
  • 2
    @user3834459 Depends on what the function does. If the functionality between the two overloads requires (or is optimised for) the value category, then yes, else probably not. In general, given the two overloads, one is left to assume that there is some difference and it would be better to call the correct version. – Niall Oct 24 '14 at 15:27