Search code examples
c++c++11rvalue-referencervaluelvalue

Why const double && doesn't work for lvalue reference?


Explain me, please, how it works? Why double && works for lvalue and rvalue? And why const double && don't work for lvalue?

template <typename U>
void function(U& var) {
    std::cout << var << std::endl;
}

int main()
{
    int var1 = 45;
    function(var1);
    function(45); //error
}

error: invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’ function(45); ////////////////////////////////////////////////

template <typename U>
void function(const U& var) {
    std::cout << var << std::endl;
}

int main()
{
    int var1 = 45;
    function(var1);
    function(45);
}

////////////////////////////////////////////////

template <typename U>
void function(U&& var) {
    std::cout << var << std::endl;
}

int main()
{
    int var1 = 45;
    function(var1);
    function(45);
}

////////////////////////////////////////////////

template <typename U>
void function(const U&& var) {
    std::cout << var << std::endl;
}

int main()
{
    int var1 = 45;
    function(var1);  // error
    function(45);
}

error: cannot bind ‘int’ lvalue to ‘const int&&’ function(var1);


Solution

  • double && does not work for lvalues. However, you don't have double && in your code, you have U && for a deduced U. That is special syntax for a so-called forwarding reference.

    There's a special rule in C++ template deduction rules which says that when the parameter type is T&& where T is a deduced type, and the argument is an lvalue of type X, then X& is used instead of X for type deduction.

    In your case, this means U is deduced to be int&, so the type of parameter var is collapsed from "int& &&" into int &.

    You can learn more about this mechanism by searching for terms such as "forwarding reference" (historically called "universal reference") or "perfect forwarding."