Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: conversions of lvalue references and rvalue references

I wonder which parts of the standard specify that in the following code segment:

#include <memory>

class A { };

class B : public A { };

int main()
{
    std::unique_ptr<B> bptr = std::make_unique<B>(); // (a)
    std::unique_ptr<A> aptr = std::move(bptr);       // (b)
    std::unique_ptr<A> &aptr_r = bptr;               // (c)
    std::unique_ptr<A> &&aptr_rr = std::move(bptr);  // (d)
    return 0;
}

(d) compiles and (c) does not. Please include the relevant parts of the standard in your answer or refer to them appropriately. Just for reference, Ubuntu clang version 3.6.2-1 (tags/RELEASE_362/final) (based on LLVM 3.6.2) gives me

error: non-const lvalue reference to type 'unique_ptr<A>' cannot
       bind to a value of unrelated type 'unique_ptr<B>'
       std::unique_ptr<A> &aptr_r = bptr;
                           ^        ~~~~

and gcc (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010 gives me

error: invalid initialization of reference of type ‘std::unique_ptr<A>&’
       from expression of type ‘std::unique_ptr<B>’
       std::unique_ptr<A> &aptr_r = bptr;
                                    ^

Edit:

To make my question more clear, let me add

class C { };

std::unique_ptr<C> cptr = std::make_unique<C>(); // (e)
std::unique_ptr<A> &&aptr_rr2 = std::move(cptr); // (f)

What is keeping (f) from compiling when (d) does? Obviously A and C are unrelated, but where is that detected when the std::unique_ptr constructor used to construct the temporary for both (d) and (f) is

template<class U, class E>
unique_ptr(unique_ptr<U, E> &&u);
like image 520
apriori Avatar asked Jan 22 '16 16:01

apriori


People also ask

What is the difference between L value and R value references?

“l-value” refers to a memory location that identifies an object. “r-value” refers to the data value that is stored at some address in memory. References in C++ are nothing but the alternative to the already existing variable. They are declared using the '&' before the name of the variable.

Can you pass an lvalue to an rvalue reference?

In the example, the main function passes an rvalue to f . The body of f treats its named parameter as an lvalue. The call from f to g binds the parameter to an lvalue reference (the first overloaded version of g ). You can cast an lvalue to an rvalue reference.

What is the relationship between lvalue and rvalue in C?

An lvalue (locator value) represents an object that occupies some identifiable location in memory (i.e. has an address). rvalues are defined by exclusion. Every expression is either an lvalue or an rvalue, so, an rvalue is an expression that does not represent an object occupying some identifiable location in memory.

What is the relationship between lvalue and rvalue?

An lvalue refers to an object that persists beyond a single expression. An rvalue is a temporary value that does not persist beyond the expression that uses it.


1 Answers

The key difference between your cases lies in the fact that rvalue references can bind indirectly (via a temporary), while non-const lvalue references cannot. In both (c) and (d), the initializer is not similar or convertible to the type referred to as determined in [dcl.init.ref]/(5.1), therefore [dcl.init.ref]/(5.2) must apply - immediately ruling out (c):

Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.

Note also that unique_ptr<A> and unique_ptr<B> are distinct, unrelated types, regardless of how A and B are related.

You can observe that rule with scalars, too:

int&& i = 0.f; // Ok
int& i = 0.f; // Not ok
like image 140
Columbo Avatar answered Oct 20 '22 23:10

Columbo