Universal reference template type always evaluate to lvalue

292 Views Asked by At

My code:

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

void store(std::string & val)
{
    std::cout << "lvalue " << val << '\n';
}

void store(std::string && val)
{
    std::cout << "rvalue " << val << '\n';
}

template<typename T> void print(T && val)
{
    std::cout << std::boolalpha << std::is_lvalue_reference<T>::value << " ";
    store(std::forward<T>(val));
}

int main()
{
    std::string val("something");
    print(val);
    print("something else");
}

my output:

true lvalue something
true rvalue something else

I've read on Universal referencing and understand why T is a lvalue when the input is a lvalue but I don't understand how is T a lvalue when the input is a rvalue, how does that collapse to the right paramter?

2

There are 2 best solutions below

0
On BEST ANSWER

I don't understand how is T a lvalue when the input is a rvalue, how does that collapse to the right paramter?

No, the input is not an rvalue.

String literals are lvalues, and that's what you pass as a parameter to the function. That's why "true" is printed.

Now, recall that string literals cannot be modified (String Literals).

So, "something else" is of lenght 14 + 1 (for the null terminator), thus a const char[15].

Now, since string literals cannot be modified, the reference will be deduced as:

 const char(&)[15];

The prototype of your method is:

store(std::string && val)

which creates a temporary std::string from const char.

2
On

The problem is not the forwarding reference. It's that you aren't passing a std::string rvalue to the function.

You are passing a string literal that is 15 character long, its type is const char[15]. And string literals are non-modifiable lvalues, so the reference is deduced as const char (&)[15].

The reason you see your rvalue overload printing is because, well, you can construct a temporary std::string out of a character array.