Why Does an Lvalue Cast Work

Why does an lvalue cast work?

It shouldn't work. An explicit type conversion to float with cast notation will be a prvalue (§5.4):

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.

My emphasis added.

The assignment operator requires an lvalue as its left operand (§5.17):

All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand.

A prvalue is not an lvalue.

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.

Casting a pointer does not produce an lvalue. Why?

An even better example, unary + yields an rvalue, as does x+0.

The underlying reason is that all these things, including your cast, create a new value. Casting a value to the type it already is, likewise creates a new value, never mind whether pointers to different types have the same representation or not. In some cases, the new value happens to be equal to the old value, but in principle it's a new value, it's not intended to be used as a reference to the old object, and that's why it's an rvalue.

For these to be lvalues, the standard would have to add some special cases that certain operations when used on an lvalue result in a reference to the old object, instead of a new value. AFAIK there's no great demand for those special cases.

Approved way to avoid lvalue cast warnings and errors?

The JoGusto answer is (a) not very good, and (b) on a C question.

First, is it legal C and C++ circumvent the lvalue cast error like that (indirection then dereferencing)?

IDK what you mean by "circumvent the lvalue cast error". The code (T)x = y; is just illegal nonsense (except for the case in C++ where T is an lvalue reference, as Joseph Mansfield's answer covers). You don't circumvent it; you write code that has a sensible meaning and does what you want to do.

The code *(T *)ptr = y; compiles. It means to invoke the assignment operator on a T object which is stored at the address in ptr. It's the same as (T &)*ptr = y; in C++, which is reinterpret_cast<T&>(*ptr) = y; .

does it violate other rules, like GCC's anti-aliasing rules?

The behaviour is subject to alignment and strict aliasing. If there is not actually a T object stored at that address, nor an object of type compatible with T according to the list in the strict aliasing rule (in this case: int or unsigned int), then it is undefined behaviour.

then what is the approved way to do it (perhaps a memcpy or memmove)?

You could write:

int x = some_integer_expression;
memcpy(chrPtrValue, &x, sizeof x);

I can usually differentiate between lvalues and rvalues, and not xvalues and prvalues.

Which expressions are you having trouble identifying in this example?

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.

C/C++ What does casting do in the low level?

If you want to get Y = 4614256650576692846, you can use:

double X = 3.14159;
long long Y = *( (long long*)(&X) );

This will cast a double pointer to a long long pointer, and then the compiler thinks that (long long*)(&X) is somewhere a long long stores.

But I don't advise you to do so because the result is based on how double is stored on your machine, and the result is not guaranteed to be 4614256650576692846.

const_castchar *(char* const) not lvalue?

So I am only interested in precisely what I asked in the question, i.e., whether this is valid code or not

It's not. The result of const_cast is a glvalue (lvalue or xvalue) only when casting to a reference type.

[expr.const.cast] (emphasis mine)

1 The result of the expression const_­cast<T>(v) is of type
T. If T is an lvalue reference to object 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 and the
lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard
conversions are performed on the expression v. Conversions that can be
performed explicitly using const_­cast are listed below. No other
conversion shall be performed explicitly using const_­cast.

You don't cast to a reference type, so the result is a prvalue; not something you may assign to. And don't go casting to a reference type either; attempting to modify an object declared as const gives undefined behavior. Your program will be another sort of invalid then.



Related Topics



Leave a reply



Submit