Search code examples
c++variadic-templatesperfect-forwarding

Do I have to use std::forward when passing arguments pack to another variadic template function


How do I implement a function (foo) which passes a parameter pack to a function (bar) which finally passes that paramer pack to a class constructor? This is the code:

template <typename... Args>
void bar(Args&&... args)
{
    MyObj obj(std::forward<Args>(args)...);
    /* Do something with obj */
}

Do I have to implement foo as

template <typename... Args>
void foo(Args&&... args)
{ bar(args...); }

Or

template <typename... Args>
void foo(Args&&... args)
{ bar(std::forward<Args>(args)...); }

Solution

  • You need to use forward. forward looks like this:

    template <class T>
    constexpr T&& forward(typename std::remove_reference<T>::type& t) noexcept
    {
        return static_cast<T&&>(t);
    }
    

    (There is another overload of forward, but it is irrelevant here.)

    For each element Arg of the template parameter pack Args, Arg can be either: (T is a non-reference type)

    • T& if the argument is a non-const lvalue, so that the parameter is of type T&; or

    • const T& if the argument is a const lvalue, so that the parameter is of type const T&; or

    • T if the argument is an rvalue, so that the parameter is of type T&&.

    In the first case, forward<Arg>(arg) instantiates to:

    constexpr T& forward(T& t) noexcept
    {
        return static_cast<T&>(t);
    }
    

    resulting in a non-const lvalue to be passed.

    In the second case, forward<Arg>(arg) instantiates to:

    constexpr const T& forward(const T& t) noexcept
    {
        return static_cast<const T&>(t);
    }
    

    resulting in a const lvalue to be passed.

    In the last case, forward<Arg>(arg) instantiates to:

    constexpr T&& forward(T&& t) noexcept
    {
        return static_cast<T&&>(t);
    }
    

    resulting in an rvalue to be passed.

    In all three cases, the value category is preserved, and the value is forwarded with modification to bar.

    If you don't use forward, you will be unconditionally passing an lvalue, which is not desired.