Is a Member of an Rvalue Structure an Rvalue or Lvalue

Is a member of an rvalue structure an rvalue or lvalue?

A member of an rvalue expression is an rvalue.

The standard states in 5.3.5 [expr.ref]:

If E2 is declared to have type
“reference to T”, then E1.E2 is an
lvalue [...]
- If E2 is a non-static data member, and the type of E1 is “cq1 vq1 X”, and
the type of E2 is “cq2 vq2 T”, the
expression designates the named member
of the object designated by the first
expression. If E1 is an lvalue, then
E1.E2 is an lvalue.

Is (*&a) a lvalue or a rvalue?

Per C 2018 6.5.3.2 4 (discussing the unary * operator), the result of unary * is an lvalue:

… If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object.…

This tells us that *&a is an lvalue. However, the expression asked about in the question is (*&a), so we must consider the effect of the parentheses.

6.3.2.1 2 (discussing automatic conversions) seems to tell us that (*&a) is converted to the value in *&a and is not an lvalue:

Except when it is the operand of the sizeof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion.

However, 6.5.1 5 (discussing parenthesized expressions) contradicts this:

A parenthesized expression is a primary expression. Its type and value are identical to those of the unparenthesized expression. It is an lvalue, a function designator, or a void expression if the unparenthesized expression is, respectively, an lvalue, a function designator, or a void expression.

This is a defect in the C standard; 6.5.1 5 and 6.3.2.1 2 contradict each other. It is left to us to understand that 6.5.1 5, which is specifically about parenthesized expressions, takes precedence over the more general 6.3.2.1 2, and this is how all C implementations behave.

Thus (*&a) is an lvalue.

Return the kind of an expression in C, i.e., whether it's an rvalue or lvalue

According to the C Standard, section 6.3.2.1:

An lvalue is an expression (with an object type other than void) that potentially designates an object.

Although this is rather vague, it continues with

The name ‘‘lvalue’’ comes originally from the assignment expression E1 = E2, in which the left operand E1 is required to be a (modifiable) lvalue.

And further more

What is sometimes called ‘‘rvalue’’ is in this International Standard described as the ‘‘value of an expression’’.

What this means to me is

An lvalue is a value that supports assignment and is on the left side of the assignment operator.

An rvalue is any value not on the left hand side of the assignment operator.

So for example

#include <stdio.h>

int main() {
int array[1], *pointer, i = 42;
// ^ ^-rvalue
// lvalue

*array = i;
// ^ ^-rvalue
//lvalue

pointer = array;
// ^ ^-rvalue
//lvalue

printf("%p\t%d\n", pointer, *pointer);
// ^-rvalues-^

// causes segfault but compiles:
*(int *)4 = 2;
// ^ ^-rvalue
// lvalue

return 0;
}

As above in the comments and to answer the question fully

You'd have to use a compiler that lets you access the AST of a given compilation unit.

C++ does reference return a lvalue or rvalue?

In this context & is the address-of operator. The value of &b is a pointer of type int *. It is a prvalue. This is unrelated to use of the & symbol in a declaration (where it means the declaration of a reference) -- symbols mean different things in declarators than they do in expressions.

You cannot change the address of a variable. Variables have the same address for their whole lifetime.

why the type of obj.i and std::forward<int>(obj.i) are different?

You're actually forwarding the wrong thing. In this function:

void foo(Obj &&obj)
{
foo(std::forward<int>(obj.i));
foo(obj.i);
}

obj has name, so it's an lvalue. obj.i is an lvalue in both cases, just in the first you're explicitly casting it to an rvalue! When forward gets a reference type, you get out an lvalue. When it gets a non-reference type, you get an rvalue. You're giving it a non-reference type, so the forward here is equivalent to: foo(std::move(obj.i)); Does that make it clearer why you get an rvalue?

The question you linked, however, is about members of rvalues. To get that, you need to turn obj itself into an rvalue:

foo(std::move(obj).i);

Here, since std::move(obj) is an rvalue, std::move(obj).i is an rvalue as well.


Regardless, using forward when taking an argument by not-forwarding-reference is a little weird. Here's a more general example:

template <class O>
void foo(O&& obj) {
foo(std::forward<O>(obj).i);
}

foo(obj); // calls foo(int&)
foo(std::move(obj)); // calls foo(int&&)

In the former case, std::forward<O>(obj) is an lvalue because O is Obj&, which makes std::foward<O>(obj).i an lvalue. In the latter case, they're both rvalues.

Why rvalue reference member would be const?

Why rvalue reference member would be const?

Don't assume that it's const. You should assume that unique_ptr(const unique_ptr&) is merely the best match, from the available constructors.

Because in constructor temp is a rvalue reference

Surprise! It is not an r-value reference.

The variable temp is bound to an r-value, when the constructor is called. And now that it's a named variable, it's no longer a "temporary". It has become an l-value.

Since you know that the value was an r-value when the constructor was called, you can safely move the members, converting them back to an r-value.

C(C &&temp)
: mVector(std::move(temp.mVector))
// ^^^^^^^^^ We know that temp CAME FROM an r-value,
// so it can safely be moved.
, mSize(temp.mSize)
{}


Related Topics



Leave a reply



Submit