Rvalue to Lvalue Conversion Visual Studio

rvalue to lvalue conversion Visual Studio

A temporary object of class type is still an object. It lives somewhere in memory, which means that there's nothing unusual in the compiler being able to attach a reference to it. At physical level whether it is a const reference or non-const reference makes no difference. In other words, in cases like that the language restriction is purely conceptual, artificial. The compiler simply ignores that restriction. There's no need to "transform" anything here. The reference is simply attached directly to the object, wherever that object happens to reside.

Basically, for a class that provides the outside word with access to the value of its this pointer (or with lvalue access to *this) the behavior can be immediately and easily simulated

struct S {
S& get_lvalue() { return *this; }
};

void foo(S& s);
...

foo(S().get_lvalue());

The above code is perfectly legal and it works around the aforementioned restriction. You can think of MSVC++ behavior as being equivalent to this.

lvalue-to-rvalue conversion of an array in ISO C

Or just microsoft's C is not ISO C but some other standard C (if there exists any).

Microsoft Visual C still supports C89 [only] whereas other compilers like gcc/clang etc support C99 too which is the current Standard.

C99 [Section 6.5.17/2] says

The left operand of a comma operator is evaluated as a void expression; there is a sequence point after its evaluation. Then the right operand is evaluated; the result has its type and value.95

Thus the result of sizeof (0,arr) would be sizeof(char*)[due to the implicit lvalue to rvalue conversion /automatic decay to pointer type] not 100*sizeof(char)

sizeof(arr) would have given 100*sizeof(char) from 6.5.3.4/3

95) A comma operator does not yield an lvalue.


decided that this would be another solution to the above problem, which I tried on Microsoft Visual Studio 2008, but regardless of whether it is compiled as C or C++ code sizeof(0, arr) always yields 4.

C++03 [5.18/1] Comma Operator

The type and
value of the result are the type and value of the right operand; the result is an lvalue if its right operand is.

So sizeof(0, arr) = sizeof (arr) and which would be equal to 100* sizeof(char) and not = sizeof(char*).

So MSVC++ is giving incorrect result (in case of C++ code).

Is it valid to bind non-const lvalue-references to rvalues in C++ 11?(modified)

should that work in c++11?

No, it should not.

Foo is a custom class, I don't understand why the first two line compiles

It compiles only with MSVC. MSVC has an (arguably useful) compiler extension that allows binding lvalues of user-defined types to rvalues, but the Standard itself forbids this. See, for instance, this live example where GCC 4.7.2 refuses to compile your code.

Does the standard say anything about this?

Indeed it does. Per paragraph 8.5.3/5 of the C++11 Standard:

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

— If the reference is an lvalue reference and the initializer expression

— is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or


— has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be
implicitly converted to an lvalue of type “cv3 T3,” where “cv1 T1” is reference-compatible with “cv3
T3
” [...],


then the reference is bound to the initializer expression lvalue in the first case and to the lvalue result
of the conversion in the second case (or, in either case, to the appropriate base class subobject of
the object). [...]

[ ...]

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
. [ Example:

double& rd2 = 2.0; // error: not an lvalue and reference not const
int i = 2;
double& rd3 = i; // error: type mismatch and reference not const

—end 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.

Can reinterpret_cast (or any cast) convert xvalues to lvalues?

Update: The code is ill-formed in C++11. Answer below is for C++14. See note at the end of this answer.

I believe this code is both well-formed and well-defined. Here's why.

The result of std::move is an xvalue [1], which is a type of glvalue; and converting a glvalue to an lvalue reference with reinterpret_cast appears to be allowed by the wording of the standard:

A glvalue expression of type T1 can be cast to the type “reference to T2” if an expression of type “pointer
to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result refers
to the same object as the source glvalue, but with the specified type. [ Note: That is, for lvalues, a reference
cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with
the built-in & and * operators (and similarly for reinterpret_cast<T&&>(x)). — end note ] No temporary
is created, no copy is made, and constructors (12.1) or conversion functions (12.3) are not called.73

Since "pointer to int" can be converted to "pointer to int", this reinterpret_cast is also allowed. The standard doesn't say anything about whether the destination type has to be an lvalue reference or rvalue reference.

The result of the cast is well-defined by the paragraph above: it refers to the same object as the source glvalue---that is, a temporary int object with the value 5. ([dcl.init.ref] specifies that a temporary is created when a prvalue is bound to a reference.)

Accessing the value through the int& also doesn't violate any aliasing rules since the original object was also of type int. In fact I believe it would even be well-defined to modify the temporary through the lvalue thus obtained.

Note: The C++11 wording says "lvalue expression", not "glvalue expression". The wording with "glvalue expression" is from N3936, which is the final working draft for C++14. I'm not an expert in how the standardization process works, but I believe this means that the change of "lvalue" to "glvalue" was already voted in by the committee, and when ISO publishes the C++14 standard, it's going to be pretty similar to what it says above.

[1] Except in the rare case in which the argument is a function; in that case the result is an lvalue, since there are no function rvalues.

Why specifying a lvalue reference for *this on a member function is different from not specifying anything?

In this case:

struct A {
void f() { cout << "A::f" << endl; }
void f() const { cout << "A::f const" << endl; }
};

getA().f();

Both overloads are viable for f, but the non-const one is preferred because it requires no conversion.

But in this case:

struct B {
void f() & { cout << "B::f &" << endl; }
void f() const & { cout << "B::f const &" << endl; }
};

getB().f();

The first overload requires this to be an lvalue. But in getB().f(), the result of getB() is a prvalue, which can't bind to a non-const lvalue. So this overload is not viable and not selected.

The second overload however requires this to be a const lvalue, which a prvalue can bind to: this overload is viable and selected by the compiler.

Binding of Type&& (rvalue reference) to modifiable lvalues and

The article is wrong, an rvalue-reference cannot bind to an lvalue. Now there is the concept of reference collapsing (that does not apply here) that allows something similar in a template argument:

template <typename T>
void f(T&& x);

The argument x can bind to either an lvalue or rvalue, but when it binds to an lvalue it is not an rvalue-reference, rather the deduced type T is U&, and the two references in U& && collapse into just the lvalue reference, so the argument is of type U& (with the rvalue-reference being dropped out). This is usually called universal reference.

Again, this does not apply to the code that you quote from the article, that is incorrect.

Note that the article was written in 2009, and the standard draft at the time might have the behavior described in the article. There were a few changes from 2009 to 2011 when the current standard was finally accepted.

The local variable which is used in return statement doesn't convert to r-value implicitly to match the conversion operator

The reason that f works under the C++11 standard (link is to a close-enough draft) is this clause

[class.copy]/32

When the criteria for elision of a copy operation are met or would be met save for the fact that the source
object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to
select the constructor for the copy is first performed as if the object were designated by an rvalue. ...

And the "criteri[on] for elision of a copy operation" that is relevant in this case is

[class.copy]/31.1

  • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value

This works for f, since x in return x is the "name of a non-volatile automatic object ... with the same cv-unqualified type as the function return type"; that type is X. This does not work for g, since the return type std::string is not the type X of the object named by x.

I think it might be important to understand why this rule is here in the first place. This rule isn't really about implicitly moving function-local variables into function return values, even though that's what it literally says. It's about making NRVO possible. Consider what you would have to write for f without these rules:

X f() {
X x;
return std::move(x);
}

But then NVRO cannot apply, since you aren't returning a variable; you're returning the result of a function call! So the clause [class.copy]/32 is about making your code

X f() {
X x;
return x;
}

syntactically legal, while the semantics as described by the clause (using a move constructor) are to be ignored (assuming your implementation isn't too stupid) because we're actually just going to do NRVO, which doesn't call anything.

You see that, really, [class.copy]/32 doesn't have to work for g. Its purpose in f is to make it possible to execute zero copy/move constructors. But g has to execute the conversion operator; there's no other sensible way for the language to pull out a std::string when you give it an X. So NVRO cannot apply in g, so there's no need to write return x;, so you can just write

std::string g() {
X x;
return std::move(x);
}

and not be worried that will cause a missed optimization.

We see that the C++11 rule [class.copy]/32 is designed so that it affects the minimal portion of the cases possible. It applies to those cases where'd we'd like NVRO but don't have a copy constructor, and makes NVRO possible by telling us to pretend we'll call the move constructor. But when actually writing code, that means it's a mind-twister of a rule to remember: "to minimize copies/moves, if the return type is the same as the type of the variable, return the_variable;, and otherwise return std::move(the_variable)." That's why the C++20 standard completely rephrases [class.copy]/32 into

[class.copy.elision]/3

An implicitly movable entity is a variable of automatic storage duration that is either a non-volatile object or an rvalue reference to a non-volatile object type. In the following copy-initialization contexts, a move operation is first considered before attempting a copy operation:

  • If the expression in a return ([stmt.return]) or co_­return ([stmt.return.coroutine]) statement is a (possibly parenthesized) id-expression that names an implicitly movable entity declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, or
  • ...

overload resolution to select the constructor for the copy or the return_­value overload to call is first performed as if the expression or operand were an rvalue. ...

This does not require that the return type be the same as the variable's type for an implicit move; it can be summed up as the conceptually simpler rule "returning a variable tries to move, and then tries to copy". That leads to the conceptually simpler principle "When returning a variable from a function, just return the_variable; and it will do the right thing". (Of course, none of GCC, Clang, or MSVC seem to have gotten the memo. That has to be some kind of record...)



Related Topics



Leave a reply



Submit