One VS2010 Bug ? Allowing Binding Non-Const Reference to Rvalue Without Even a Warning

One VS2010 bug ? Allowing binding non-const reference to rvalue WITHOUT EVEN a warning?

That is a known issue/feature of the VS compilers. They have always allowed that and there does not seem to be any push into removing that extension.

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 ]

non-const reference to rvalue


What I don't understand is why does the temp1.foo_ref(temp()) succeed.

Judging by use of void main() { ... }, you must have an old or non-standards compliant compiler.

temp1.foo_ref(temp());

should fail. I get the following error using g++ 4.8.4.

g++ -Wall -std=c++11     socc.cc   -o socc

socc.cc: In function ‘int main()’:
socc.cc:13:22: error: no matching function for call to ‘temp::foo_ref(temp)’
temp1.foo_ref(temp()); //foo_ref (temp& a) is called
^
socc.cc:13:22: note: candidate is:
socc.cc:6:7: note: void temp::foo_ref(temp&)
void foo_ref(temp& a){}
^
socc.cc:6:7: note: no known conversion for argument 1 from ‘temp’ to ‘temp&’

error related to binding non-const lvalue to rvalue in constructor

The problem is the following, you are creating a temporary

make_shared<X>(NULL)

that will die after the line is executed, and the reference in your class will be dangling (i.e. referencing something that has been destroyed), if you try to access the reference you will be in undefined behavior territory (your program can crash, or worse continue in a corrupted state).
A way to fix it would be to not use references to shared_ptr but directly shared_ptr in all your classes which is way safer.

Secondly, you might want to use nullptr instead of NULL

Lastly, I think you are misunderstanding what the references are supposed to do: they are not about ownership of a resource, but simply allow access to a resource, when you have a reference to something you must be sure that somebody else is keeping that resource alive for as long as you want to access the resource via the reference (there is an exception: lifetime extension via a local const ref, see https://blog.galowicz.de/2016/03/23/const_reference_to_temporary_object/).

Why rvalue reference as return type can't be initialization of non-const reference?


I know that an rvalue reference is an lvalue.

You're talking about two different things: type and value category. e.g.

int&& ri = 0; // ri's type is rvalue reference (to int)
// ri's value category is lvalue; it's a named variable.

Given your 1st sample, what fun() returns is an xvalue, which belongs to rvalues.

The following expressions are xvalue expressions:

  • a function call or an overloaded operator expression, whose return type is rvalue reference to object, such as std::move(x);

then,

int &a = fun(); // fails; lvalue-reference can't bind to rvalue

In the 2nd sample, what fun() returns is an lvalue,

The following expressions are lvalue expressions:

  • a function call or an overloaded operator expression, whose return type is lvalue reference, such as std::getline(std::cin, str),
    std::cout << 1, str1 = str2, or ++it;

then

int & a=fun(); // fine; lvalue-reference could bind to lvalue

In the 3rd sample,

decltype(fun()) b = 1; // the return type of fun() is rvalue-reference;
// this has nothing to do with the value category of its return value
// b's type is rvalue-reference too, btw its value category is lvalue

In the 4th sample,

int &&a = 1; // fine; rvalue-reference could bind to rvalue
// a's type is rvalue-reference, its value category is lvalue
int &b = a; // fine; lvalue-reference could bind to lvalue
// b's type is lvalue-reference, its value category is lvalue

error: cannot bind non-const lvalue reference of type ‘Position&’ to an rvalue of type ‘Position’

Your update() is looking to take a Position by reference so that it can make modifications and have them percolate back to the object you're passing. However, tortoise->getCurrPos() returns a Position copy. If you want the changes in update() to affect your tortoise's Position member, you'll need to have getCurrPos() return its Position by reference.

Position Runner::getCurrPos() { 

Should become

Position& Runner::getCurrPos() { 

For more, you can read up at What is a reference variable in C++?

Allowed to bind an rvalue to a non-const lvalue reference?

This is a compiler "extension" (or "bug", depending on your perspective) of the Microsoft compiler. C++ only allows non-const binding of an lvalue to a non-const lvalue reference.

std::tie fails with cannot bind non-const lvalue reference when passed value from a function call

std::tie takes lvalue references as arguments, so int returned by S::y can't bind. You could use the perfect forwarding version, std::forward_as_tuple, instead:

#include <tuple>

struct S
{
int x = 0;
int y() const { return 1; }
};

bool f(const S& a, const S& b)
{
return std::forward_as_tuple(a.x, a.y()) < std::forward_as_tuple(b.x, b.y());
}

Demo.



Related Topics



Leave a reply



Submit