Search code examples
c++c++11templatesperfect-forwarding

In C++, what is the correct way to do perfect forwarding an lvalue to a function template?


What is the correct way to accept a parameter pack for perfect-forwarding, such that it will take any type and simply forward them? The following code works on regular types but fails on pointer types:

template<typename ...A>
void b(A &&... args)
{}

template<typename ...A>
void a(A &&... args)
{
    b(std::forward<A>(args)...);
}


int main() {

    // ok
    a<int>(5);

    // error: cannot bind rvalue reference of type ‘int*&&’ to lvalue of type ‘int*’
    int *foo = nullptr;
    a<int*>(foo);

    return 0;
}

[edit] Thanks for the quick and great replies! I oversimplified - this is a closer example of the problem I am trying to solve:

#include <iostream>
using namespace std;

template<typename F>
struct fun;

template<typename F, typename ...A>
struct fun<F(A...)>
{
    void b(A &&... args)
    {}
    
    void a(A &&... args)
    {
        b(std::forward<A>(args)...);
    }
};

int main() {

    // ok
    fun<void(int)> f1;
    f1.a(5);

    // error: cannot bind 'int' lvalue to 'int&&'
    fun<void(int)> f2;
    int x = 5;
    f2.a(x);

    return 0;
}

In this case, I don't have the luxury to let the template adjust automatically... Any idea how this can be achieved?

[edit 2] as pointed out in the comments, this has nothing to do with pointers, I updated my sample to simply use an lvalue


Solution

  • You should not specify template arguments explicitly; which just prevents template argument deduction from working with forwarding reference, and yields unexpected result.

    a<int>(5);    // A is specified as int then function parameter's type is int&&.
                  // 5 is an rvalue and could be bound to int&&
    
    a<int*>(foo); // A is specified as int* then function parameter's type is int* &&.
                  // foo is an lvalue and couldn't be bound to int* &&
    

    Just

    a(5);   // 5 is rvalue, then A is deduced as int and function parameter's type collapses to int&&
    
    int *foo = nullptr;
    a(foo); // foo is lvalue, then A is deduced as int* & and function parameter's type collapses to int* &
    

    EDIT

    Firstly both the member functions b and a are not template, and their parameters are not declared as forwarding reference at all.

    The code doesn't work because

    fun<void(int)> f2;
    int x = 5;
    f2.a(x); // A is specified as int then function parameter's type is int &&.
             // x is an lvalue and couldn't be bound to int &&
    

    I'm not sure about your intent, you can change it to

    fun<void(int&)> f2;
    //          ^
    int x = 5;
    f2.a(x); // A is specified as int& then function parameter's type collapses to int&.
             // x is an lvalue and could be bound to int&
    

    Or make them function templates and still apply forwarding reference.

    template <typename... T>
    void b(T &&... args)
    {}
    
    template <typename... T>
    void a(T &&... args)
    {
        b(std::forward<T>(args)...);
    }