Why does rvalue reference argument prefer const lvalue reference to rvalue reference parameter?

729 views Asked by At

Here is some code in VS2015:

class Elem 
{
public:
    Elem(int i) : a(i) {}
    Elem(Elem && other) = default;
    Elem(const Elem & other) = default;
private:
    int a;
};

int main()
{
    std::vector<Elem> vv;

    // case 0
    vv.push_back(Elem{ 0 }); // call push_back(Elem &&);

    // case 1
    Elem e1{ 1 };
    vv.push_back(e1); // call push_back(const Elem &);

    // case 2
    Elem e2{ 2 };
    auto & lref = e2;
    vv.push_back(lref); // call push_back(const Elem &);

    // case 3
    Elem e3{ 3 };
    auto && rref = std::move(e3);
    vv.push_back(rref); // call push_back(const Elem &);

    // case 4
    Elem e4{ 4 };
    vv.push_back(std::move(e4)); // call push_back(Elem &&);

    return 0;
}

In case 3, the type of rref is rvalue reference, and its value category is lvalue, and calls push_back(const Elem &).

In case 4, According to Effective Modern C++ Item 23, an implementation of std::move is something like

// C++ 14    
template<typename T>
decltype(auto) move(T&& param)
{
    using ReturnType = remove_reference_t<T>&&;
    return static_cast<ReturnType>(param);
}

The type of std::move(e4) is Elem &&, and its value category is prvalue, calls push_back(Elem &&).

So a lvalue of T&& matches const T &, and a prvalue of T&& matches T&&, what do type and value category of an expression actually do during overload resolution between T, const T & and T &&?


Sorry for not describing my problem clearly. As so many links said that, if an argument's value category is prvalue, the function with T&& will be called; value category is lvalue, the function with const T & will be called.

Can I simply say that argument's type is used for overload resolution, while value category is checked for reference binding when parameter's type is a reference?

2

There are 2 answers

0
ytoledano On

rref is an lvalue since it has a name, so push_back(const Elem &) is called. The move into a named variable is useless, but if you insist on it you can change your code to:

// case 3
Elem e3{ 3 };
auto && rref = std::move(e3);
vv.push_back(std::forward<decltype(e3)>(rref));

And then push_back(Elem &&) is called. Or just do what you did in case 4.

0
Neo On

The moment when u assign a variable name(in this case "e3"), and use this variable to pass in the function it becomes lvalue. That's why its calling the lvalue version of function "push_back". But when u are passing as "std::move(e4)" in Case 4, it is passed as rvalue and that's why its calling the rvalue reference version of "push_back" function.

just going through the web i found this link which explains rvalue reference in almost similar terms. u can check this link.

http://simpletechtalks.com/rvalue-references/