Search code examples
c++rvalue-referencestd-pair

Why binding rvalue to a const type makes it an lvalue?


This question may not have the best title but here is the code that will explain what I am trying to ask.

This code runs and prints "lvalue" but If I remove const from MyPair's first type, it gives me the expected output, which is "rvalue". I would like to know what role const plays here?

#include <iostream>
#include <utility>
#include <string>


using MyPair = std::pair<const std::string, int>;


void func(std::string&& str)
{
    std::cout << "rvalue" << std::endl;
}

void func(const std::string& str)
{
    std::cout << "lvalue" << std::endl;
}

template<typename T>
void func_forward(T&& p)
{
    func(std::forward<T>(p).first);
}

void test(MyPair&& p)
{
    func_forward(std::move(p));
}

int main()
{
    test({"hello", 3});
    return 0;
}

Solution

  • Nothing is being turned into a lvalue.

    std::forward<T>(p).first is in your example's instantiation is a rvalue (specifically xvalue) regardless of the const. However, it's type will be const std::string or std::string depending on the choice of const in the MyPair type.

    The problem is that your method of differentiating lvalues from rvalues with func is incorrect, because the first overload of func's parameter is not const-qualified and so will only accept rvalues of non-const std::string type.

    Your second func overload on the other hand can accept any value category and const-qualification because a const lvalue reference is allowed to bind to lvalues as well as rvalues. So it will be chosen if first is const qualified, regardless of value category as the the only viable overload.

    To fix your detection method use const std::string&& instead of std::string&& as parameter type in the first func overload.