Search code examples
c++templatesc++11perfect-forwarding

passing rvalue raises cannot bind to lvalue


The program is as below:

#include <iostream>
using namespace std;

template <typename F, typename T1, typename T2>
void flip2(F f, T1 &&t1, T2 &&t2)
{
      f(t2, t1);
}

void g(int &&i, int &j)
{
      cout << i << " " << j << endl;
}

int main(void)
{
      int i = 1;
      flip2(g, i, 42);
}

The compiler complains:

error: rvalue reference to type 'int' cannot bind to lvalue of type 'int'

But to my understanding, as T2 is instantiated with int, then the type of t2 is int&&, so it should be allowed to pass to function g's first argument (int &&).

What's wrong with my understanding?


Solution

  • f(t2, t1);
    

    t2 has a name, so it is an lvalue. It's type is rvalue, but in an expression it's type is an lvalue. In order to pass it as an rvalue reference, you need to use std::forward (move or casting would be inappropriate here, because T1 and T2 are actually universal references, not rvalue references, see edit).

    #include <iostream>
    using namespace std;
    
    template <typename F, typename T1, typename T2>
    void flip2(F f, T1 &&t1, T2 &&t2)
    {
          f(std::forward<T2>(t2), std::forward<T1>(t1));
    }
    
    void g(int &&i, int &j)
    {
          cout << i << " " << j << endl;
    }
    
    int main(void)
    {
          int i = 1;
            flip2(g, i, 42);
    }
    

    http://ideone.com/Aop2aJ

    Why

    Consider:

    template<typename T>
    void printAndLog(T&& text) {
        print(text);
        log(text);
    }
    
    int main() {
        printAndLog(std::string("hello, world!\n"));
    }
    

    When you use a variable's name, the expression-type is lvalue (glvalue?); the rvalueness is discarded. Otherwise in the example above, we'd have lost text to print(). Instead, we have to be explicit when we want to our rvalue to behave like one:

    template<typename T>
    void printAndLog(T&& text) {
        print(text);
        log(std::forward<T>(text));  // if text is an rvalue, give it up.
    }
    

    Edit

    I used std::forward because T1&& and T2&& are universal references, not rvalue references. https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers