Need Meyers Effective C ++ Widget Rvalue Example Explain

I have a small question in C ++.

There is an example on the first pages of Effective Modern C ++:

class Widget {
public:
    Widget(Widget&& rhs);
};

      

Also, there is a comment: 'rhs is an lvalue even though it is of an rvalue reference type'.

I just didn't understand anything, to be honest. What does it mean: "rhs is an lvalue, but is it an rvalue reference ??

+3


source to share


2 answers


Be aware that there are two different things here:

  • One is related to the type of variables: there are two types of references: lvalue ( &

    ) references and rvalue ( &&

    ) references .

    This defines what the function preferably accepts and is always "obvious", because you can simply read it from the type signature (or use it decltype

    ).

  • Another is the property of expressions (or values): an expression can be an lvalue or an rvalue ( actually, it's harder than that ... ).

    This property is not directly visible in the code (but there is a rule, see below), but you can see its effects in overload resolution. In particular,

    • lvalue arguments prefer to bind to lvalue-reference parameters, whereas
    • rvalue arguments prefer to bind to rvalue-reference parameters.

These properties are closely related (and in a sense "dual" with each other), but they are not necessarily consistent with each other. In particular, it is important to understand that variables and expressions are actually different, so formally speaking, they are not even comparable, "apples to oranges".




There is such a rule in C ++ that even though you have specified rhs

as an rvalue reference (which means it will preferably match arguments that are rvalues) inside a move constructor block, the variable itself rhs

will still behave like lvalues, and thus preferably correspond to functions that accept lvalue references.

void test(Widget&&) { std::cout << "test(Widget&&): called\n"; }
void test(Widget&)  { std::cout << "test(Widget&): called\n"; }

Widget::Widget(Widget&& rhs) {
    // here, `rhs` is an lvalue, not an rvalue even though
    // its type is declared to be an rvalue reference

    // for example, this will match `test(Widget&)`
    // rather than the `test(Widget&&)` overload, which may be
    // a bit counter-intuitive
    test(rhs);

    // if you really want to match `test(Widget&&)`
    // you must use `std::move` to "wrap" the variable
    // so that it can be treated as an rvalue
    test(std::move(rhs));
}

      

The rationale for this was to prevent unintended moves within the move constructor.

As a general rule of thumb, if an expression has a name (that is, it consists of a single named variable), then it is an lvalue. If the expression is anonymous, then it is an rvalue. (As dyp pointed out, this is not technically correct - see his comment for a more formal description.)

+7


source


Short and simple explanation: P

Widget(Widget&& rhs);

      



is a move constructor. It will take an rvalue as a parameter. Inside the definition of a move constructor, you can refer to another widget using the name rhs, so it is an lvalue.

+2


source







All Articles