Search code examples
c++templatesperfect-forwardingforwarding-reference

Why can't I use universal reference in a way like `void(*func)(T&&)` and `for (T&& v : vec)`?


I'm trying to use a universal reference in a callback function pointer and range-based for loop.

template<typename T>
void PrintFunc(const T& value) {
    cout << value << "\n";
}

template<typename T>
void PrintFunc(T&& value)  {
    cout << value << "\n";
};

template<typename T>
void ForEach(const vector<T> vec, void(*func)(T&&)) {
    for (T&& val : vec)
    {
        func(std::forward(val));
    }
}

int main() {
    vector<int> v{ 1, 2, 3 };
    ForEach<int>(v, PrintFunc<int>);
    return 0;
}

The code fails to compile:

Error C2672 'forward': no matching overloaded function found

Error C2440 'initializing': cannot convert from 'const int' to 'int &&'

I tried:

template<typename T>
void ForEach(const vector<T> vec, void(*func)(const T&)) {
    for (const T& val : vec)
    {
        func(val);
    }
}

And this time the code compiles and works properly.


Solution

  • Forwarding references only arise when applied directly to a deduced template parameter. That isn't the case with your function pointer.

    Forwarding references aren't a "real" type. They're a trick of the way template type deduction works. The way forwarding references work is by deducing different types depending on the value category of the expression passed to them.

    That is, given a function like:

    template <typename T>
    void func(T&& arg);
    

    When you call func(some_lvalue), where some_lvalue is an int, T gets deduced as int&. That leaves func's parameter as an int& &&. Since you can't have references to references, the reference collapsing rules are applied, and the type of arg collapses to int&.

    On the other hand, if you call func(42), T gets deduced as int, leaving the type of arg as int&&.


    For users not familiar with this stuff, see the old term "Universal References" coined by Scott Meyers documented on isocpp and this video:
    C++ and Beyond 2012: Scott Meyers - Universal References in C++11.