Is the Result of a Cast an Rvalue

Is the result of a cast an rvalue?

The should be an rvalue but webcompiler is running Visual Studio and Visual Studio has an extension which allows temporary objects to be bound to non-const lvalue references. a bug/extension that casues it to generate an lvalue in this case As Igor points out above this can be disabled using /Za (see it live).

We can see that it should be an rvalue(specifically a prvalue) from the draft C++ standard section 5.4 Explicit type conversion (cast notation) paragraph 1 which says (emphasis mine):

The result of the expression (T) cast-expression is of type T. The
result is an lvalue if T is an lvalue reference type or an rvalue
reference to function type and an xvalue if T is an rvalue reference
to object type; otherwise the result is a prvalue.[ Note: if T is
a non-class type that is cv-qualified, the cv-qualifiers are ignored
when determining the type of the resulting prvalue; see 3.10. —end
note ]

Both gcc and clang result in rvalue which is the expected result.

As an aside, I would recommend using rextester over webcompiler since rextester allows you to share your program and also has live sharing.

Update

Ben Voigt point out this bug report and so it seems that Visual Studio actually produces an lvalue. So this is not simply a case of the extension which allows temporary objects to be bound to non-const lvalue references.

As dyp points out gcc used to have a cast to lvalue extension as well.

Update 2

Mgetz filed a bug report, the response was that this is fixed by using the /Zc:rvalueCast flag, the description of the flag is as follows:

When the /Zc:rvalueCast option is specified, the compiler correctly
identifies an rvalue reference type as the result of a cast operation
in accordance with the C++11 standard. When the option is not
specified, the compiler behavior is the same as in Visual Studio 2012.
By default, /Zc:rvalueCast is off. For conformance and to eliminate
errors in the use of casts, we recommend that you use /Zc:rvalueCast.

It is unclear whether this flag will be enabled by default in future versions.

Is it an Rvalue or Lvalue After a Cast

From expr.cast (this is applicable from C++11 and later)

The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type and an xvalue if T is an rvalue reference to object type; otherwise the result is a prvalue. [ Note: If T is a non-class type that is cv-qualified, the cv-qualifiers are discarded when determining the type of the resulting prvalue; see Clause [expr]. — end note ]


For C++98:

The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is a reference type, otherwise the result is an rvalue. [ Note: if T is a non-class type that is cv-qualified, the cv-qualifiers are ignored when determining the type of the resulting rvalue; see 3.10. — end note ]

Then, gcc is right


From mkaes's comment, it seems like this is the (arguably useful) MSVC extension

Why isn't the result of this cast an lvalue?

Old versions of gcc support something called "lvalue casts" -- if you cast something that is an lvalue the result is an lvalue and can be treated as such. The main use for it is allowing you to increment a pointer by an amount corresponding to a different size:

int *p;
++(char *)p; /* increment p by one byte, resulting in an unaligned pointer */

This extension was deprecated some time around gcc v3.0 and removed in gcc v4.0

To do the equivalent thing in more recent versions of gcc, you need do an addition and assignment (instead of an increment) casting the pointer to the type for the addition and back for the assignment:

p = (int *)((char *)p + 1);

Note that trying to dereference the pointer after this is undefined behavior, so don't count on it doing anything useful.

Why cast expression to rvalue reference to function is lvalue?

It follows from the definition of value categories in C++11 3.10/1 (emphasis mine):

  • An lvalue (...) designates a function or an object. ...

  • An xvalue (an “eXpiring” value) also refers to an object, ...

  • An rvalue (...) is an xvalue, a temporary object (12.2) or subobject thereof, or a value that is not associated with an object.

  • A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. ...

Notice that only the lvalue category can be a function, all the others are values or objects only.

It's also echoed in 5.2.9/1:

The result of the expression static_cast<T>(v) is the result of converting the expression v to type T. If T
is
an lvalue reference type or an rvalue reference to function type, the result is an lvalue; if T is an rvalue
reference to object type, the result is an xvalue; otherwise, the result is a prvalue. ...

As for the why of it, I can of course only guess (not being part of the standardisation committee). But my guess is that it would make no sense to have rvalues of function type—a function can never be a temporary, it can never be at or near the end of its lifetime.

is there any difference between static cast to rvalue reference and std::move

Yes there is a very important difference: std::move documents what you want to do. In addition the cast is prone to writing errors like a forgotten & or wrong type X.

As it can be seen, std::move is even less to type.

How does static_casttype&&(x) convert l valued r-value reference to x value type what is the internal logic?

[expr.static.cast]/1 The result of the expression static_cast<T>(v) is the result of converting the expression v to type T. If T is an lvalue reference type or an rvalue reference to function type, the result is an lvalue; if T is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue.

Emphasis mine. Therefore, static_cast<type&&>(x), where type is an object type (as opposed to a function type), is an xvalue by definition.

dynamic_cast and rvalue reference

Your code is, of course, fine:

If T is an rvalue reference type, v shall be an expression having a
complete class type, and the result is an xvalue of the type referred
to by T.

Presumably, dynamic_cast wasn't updated properly when rvalue references were introduced, and still enforces the pre-C++11 rule that rvalues shall only be bound to const lvalue references (note that it doesn't even work when altering the target type to B const&&, despite that being implied by the error message!).

Filed as #69390.



Related Topics



Leave a reply



Submit