HerpDerpington
HerpDerpington

Reputation: 4453

Can rvalue references be passed on as const references?

Consider the following example struct:

struct A {
    int i;

    void init(const A &a)
    {
        this->i = a.i;
    }

    A(int i)
    {
        this->i = i;
    }

    A(const A &a) = delete;
    A &operator=(const A &a) = delete;

    A(A &&a)
    {
        init(a); // Is this allowed?
    }

    A &operator=(A &&a)
    {
        init(a); // Is this allowed?
        return *this;
    }
};

The rvalue reference A &&a is passed on to a function accepting a const A &a, i.e. a constant lvalue reference. Is this allowed and resulting in well defined behaviour in C++?

Upvotes: 1

Views: 308

Answers (2)

Apollys supports Monica
Apollys supports Monica

Reputation: 3258

You need to cast a to an rvalue in your example to get the intended effect, because variable names themselves are lvalues (this is a tricky detail of lvalues and rvalues in C++). So as written, it's correct C++ but not doing what you think it's doing.

If you cast it, using std::move(a) instead of just a, the code now does what you want and is still correct. This is because of a special rule in C++ that temporaries can be bound to const lvalues, a more detailed discussion of which can be found here. This features comes as a great convenience when you have code like:

void ProcessData(const std::vector<int>& input_vector);

and then you want to test it with the following:

ProcessData(std::vector<int>{1, 2, 3, 4, 5});

This saves you from having to explicitly create the object before passing it on as a const lvalue reference. Note the const here is critical, without it the code is incorrect. There is a more detailed discussion of this rationale for this choice here.

Upvotes: 1

geza
geza

Reputation: 29962

Yes, it is allowed.

Note, that value category of the expression a is lvalue, even though a's declared type is rvalue reference.

Furthermore, if you create an rvalue from a with std::move, your code is still well-formed, as an rvalue can be bound to a const lvalue reference:

init(std::move(a)); // std::move(a) is an rvalue (to be precise, xvalue), but still OK

Upvotes: 1

Related Questions