Changkun
Changkun

Reputation: 1612

Why rvalue reference pass as lvalue reference?

pass() reference argument and pass it to reference, however a rvalue argument actually called the reference(int&) instead of reference(int &&), here is my code snippet:

#include <iostream>
#include <utility>
void reference(int& v) {
    std::cout << "lvalue" << std::endl;
}
void reference(int&& v) {
    std::cout << "rvalue" << std::endl;
}
template <typename T>
void pass(T&& v) {
    reference(v);
}
int main() {
    std::cout << "rvalue pass:";
    pass(1);

    std::cout << "lvalue pass:";
    int p = 1;
    pass(p);

    return 0;
}

the output is:

rvalue pass:lvalue
lvalue pass:lvalue

For p it is easy to understand according to reference collapsing rule, but why the template function pass v to reference() as lvalue?

Upvotes: 7

Views: 946

Answers (2)

WhiZTiM
WhiZTiM

Reputation: 21576

template <typename T>
void pass(T&& v) {
    reference(v);
}

You are using a Forwarding reference here quite alright, but the fact that there is now a name v, it's considered an lvalue to an rvalue reference.

Simply put, anything that has a name is an lvalue. This is why Perfect Forwarding is needed, to get full semantics, use std::forward

template <typename T>
void pass(T&& v) {
    reference(std::forward<T>(v));
}

What std::forward<T> does is simply to do something like this

template <typename T>
void pass(T&& v) {
    reference(static_cast<T&&>(v));
}

See this;

Upvotes: 8

Rakete1111
Rakete1111

Reputation: 48938

Why the template function pass v to reference() as lvalue?

That's because v is an lvalue. Wait, what? v is an rvalue reference. The important thing is that it is a reference, and thus an lvalue. It doesn't matter that it only binds to rvalues.

If you want to keep the value category, you will have to do perfect forwarding. Perfect forwarding means that if you pass an rvalue (like in your case), the function will be called with an rvalue (instead of an lvalue):

template <typename T>
void pass(T&& v) {
    reference(std::forward<T>(v)); //forward 'v' to 'reference'
}

Upvotes: 7

Related Questions