C++: rvalue reference memory - c++

Since c++ provides references to rvalues i.e. rvalue references which are mainly used to perform move semantics and other memory efficient tasks. But in the following case reference is changing the value of an literal but as we know that literals are read only so how could a reference change the value of some read only variable. Does a rvalue reference allocate it's own memory or it simply changes the value of literal?
#include <iostream>
using namespace std;
int main()
{
int a = 5;
int&& b = 3;
int& c = a;
b++;
c++;
cout << " Value for b " << b << " Value for c " << c << endl;
}
Secondly, when a temporary object is assigned with a reference, reference works with the data of that object. But according to the definition of temporary objects they are deleted as when the expression using them ends. How could the reference act as an alias name to that temporary object if that temporary object is out of memory?

Numeric literals can't be bound to any reference, neither an rvalue reference nor an lvalue reference. Conceptually, a numeric literal creates a temporary object initialized from the literal value and this temporary can be bound to an rvalue references or to const lvalue reference (int const& r = 17;). It seems the relevant quote on literals is 5.1.1 [expr.prim.general] paragraph 1:
A literal is a primary expression. Its type depends on its form (2.14). A string literal is an lvalue; all other literals are prvalues.
When binding a reference directly to a temporary, it's life-time gets extended until the reference goes out of scope. The relevant section for the life time issue is 12.2 [class.temporary] paragraph 5:
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists
for the lifetime of the reference except:
A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits.
A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full-expression containing the call.
The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
A temporary bound to a reference in a new-initializer (5.3.4) persists until the completion of the full-expression containing the new-initializer.

Related

Does a constant reference member variable in an anonymous struct extend the lifetime of a temporary?

Consider the following code:
struct Temp{ int i = 0; };
Temp GetTemp() { return Temp{}; }
int main()
{
for (struct{Temp const & t; int counter;} v = {GetTemp(), 0};
v.counter < 10;
++v.counter)
{
// Is v.t guaranteed to not be a dangling reference?
std::cout << v.t.i << std::endl;
}
}
So GetTemp() returns a temporary object, which then gets assigned to a constant reference variable. However, that constant reference variable is a member of an anonymous local struct. Question: Does the C++ standard guarantee that the lifetime of that temporary gets extended till after the loop terminates?
Considering this question, I would have expected the answer to be no, i.e. that I get a dangling reference in the loop body. However, gcc and clang seem to extend the lifetime (see example on godbolt), and even do not complain with -fsanitize=undefined, which surprised me.
For braced aggregate initialization as in your example, lifetime extension has been guaranteed since C++98 (irrespective of the linkage/visibility properties of the class). This is intuitive, since the reference is directly bound to the temporary, and not via some intermediate ctor parameter, as in the question you've linked. For legalese, see [class.temporary] in the C++14 FD that outlines the lifetime extension contexts.
See also the note here which differentiates braced and parenthetical initialization since C++20:
[Note 7: By contrast with direct-list-initialization, narrowing
conversions ([dcl.init.list]) are permitted, designators are not
permitted, a temporary object bound to a reference does not have its
lifetime extended ([class.temporary]), and there is no brace elision.
[Example 3:
struct A {
int a;
int&& r;
};
int f();
int n = 10;
A a1{1, f()}; // OK, lifetime is extended
A a2(1, f()); // well-formed, but dangling reference
Does a constant reference member variable in an anonymous struct extend the lifetime of a temporary?
Yes, the chain of const references is unbroken. There's is only v and v is alive until the end of the for loop and the lifetime of the referenced Temp is therefore extended until then. The fact that the struct is anonymous has no impact.
class.temporary/4
There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression. The first context is when a default constructor is called to initialize an element of an array. If the constructor has one or more default arguments, the destruction of every temporary created in a default argument is sequenced before the construction of the next array element, if any.
class.temporary/5
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
(5.1) A temporary bound to a reference member in a constructor's ctor-initializer ([class.base.init]) persists until the constructor exits.
(5.2) A temporary bound to a reference parameter in a function call ([expr.call]) persists until the completion of the full-expression containing the call.
(5.3) The lifetime of a temporary bound to the returned value in a function return statement ([stmt.return]) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
(5.4) A temporary bound to a reference in a new-initializer ([expr.new]) persists until the completion of the full-expression containing the new-initializer.
None of the exceptions to the lifetime extension applies to your case.

Lifetime extension, prvalues and xvalues

Following the well accepted answer to this question Do rvalue references allow dangling references? It would seem that xvalues do not have their lifetime extended when assigned to a rvalue reference lvalue like in the question. However when I do this
#include <iostream>
using namespace std;
class Something {
public:
Something() {
cout << "Something()" << endl;
}
Something(const Something&) {
cout << "Something(const Something&)" << endl;
}
Something(Something&&) {
cout << "Something(Something&&)" << endl;
}
~Something() {
cout << "~Something()" << endl;
}
int a;
};
Something make_something() {
return Something{};
}
int main() {
auto&& something = make_something().a;
return 0;
}
The lifetime of the object returned by a call to make_something is extended, even though make_something().a is an xvalue as per http://en.cppreference.com/w/cpp/language/value_category (the third bullet in the xvalues explanation lists the member access I have above as an xvalue,)
a.m, the member of object expression, where a is an rvalue and m is a
non-static data member of non-reference type;
If value categories do not determine when the lifetime of an rvalue will be extended then what does? I am having a hard time understanding when the lifetime of an rvalue is extended in C++
Lifetime extension doesn't care about value categories. As stated by [class.temporary]/p6:
The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference
Emphasis added.
This says nothing here about the value category of the expression being referenced.
What determines whether a temporary is extended is exactly the above (and a few more rules).
But that does not explain why adding an std::move() around the temporary to which the reference is being assigned does not extend the lifetime
std::move is not a magical, compiler-defined construct in C++. It is a function call, and therefore it behaves no differently from any other C++ function call.
So, if you have std::move(Type()), what does that mean? It means that you will create a temporary, bind it to the parameter of std::move, then call that function, which will return something.
Binding a temporary to a function parameter, as stated in [class.temporary]/p6, means that the lifetime of the temporary is fixed to be the lifetime of the full expression that created it (if not for that rule, then the temporary would have to be destroyed at the end of the function call, since that's the end of the reference's lifetime).
It doesn't matter what the function does, says, or implies. It doesn't matter if the compiler could perhaps inline things and determine that the return value is a reference to an argument that came from a temporary. The lifetime of that temporary is fixed to the expression, not extended.
If value categories do not determine when the lifetime of an rvalue will be extended then what does? I am having a hard time understanding when the lifetime of an rvalue is extended in C++
Note that value categories describe expressions not objects. Value categories ( xvalue, prvalue, or whatever ) won't be extended in any way. Only objects can have a lifetime.
From the n4296 standard draft:
§12.2.1
Temporaries of class type are created in various contexts: binding a reference to a prvalue (8.5.3), returning
a prvalue (6.6.3), a conversion that creates a prvalue (4.1, 5.2.9, 5.2.11, 5.4), throwing an exception (15.1),
and in some initializations (8.5).
and
§12.2.4
There are two contexts in which temporaries are destroyed at a different point than the end of the full-
expression. [...]
The second context is when a reference is bound to a temporary. The temporary to which the reference is
bound or the temporary that is the complete object of a subobject to which the reference is bound persists
for the lifetime of the reference
Note: I didn't quote the first context since it has minor revelance regarding the questions. Italic emphasis added by me.
Thus the value category of the function expression makesomething() is a prvalue creating a temporary of class type, according to the first paragraph cited above.
makesomething().a accesses a temporary, complete subobject. Binding this temporary to a reference leads, according to the second context quoated above, to an extended lifetime.
The lifetime of the subobject a is coupled to the lifetime of the previous created temporary making it an expiring value (xvalue). Without extending its lifetime by binding it to a reference it would be destroyed together with the temporary class object. Thus, in this case, after the ;.

What determines when the lifetimes of temporaries get extended into const references or rvalue references?

Given:
struct hurg { ... };
hurg get_hurg() { return hurg(); }
hurg&& get_mhurg() { return hurg(); }
My understanding and experimenting shows that the following is not undefined behavior (EDIT: thanks to the answers, it turns out I was wrong and the get_mhurg() examples are undefined behavior):
{
const hurg& a = get_hurg();
hurg&& b = get_hurg();
const hurg& c = get_mhurg();
hurg&& d = get_mhurg();
// do stuff with a, b, c, d
}
// a, b, c, d are now destructed
That is, the lifetime of the temporary hurg object returned by get_hurg() and get_mhurg() is extended until the end of the scope.
However, in the case of (function from here):
template <typename T>
auto id(T&& x) -> decltype(auto) { return decltype(x)(x); }
Using it like:
{
const hurg& x = id(hurg());
// the hurg() 'x' refers to is already destructed
hurg&& y = id(hurg());
// the hurg() 'y' refers to is already destructed
// undefined behavior: use 'x' and 'y'
}
In this case, the lifetime of the hurg is not extended.
What determines when the lifetime of temporaries is extended in general? And, in particular, when is it safe to bind the result of a function to a const lvalue ref or an rvalue ref?
And more specifically, what precisely is happening in the id case?
From [class.temporary]:
There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression.
The first context is when a default constructor is called to initialize an element of an array [...]
The second context is when a reference is bound to a temporary. The temporary to which the reference is
bound or the temporary that is the complete object of a sub-object to which the reference is bound persists
for the lifetime of the reference except:
(5.1) — A temporary object bound to a reference parameter in a function call (5.2.2) persists until the completion
of the full-expression containing the call.
(5.2) — The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not
extended; the temporary is destroyed at the end of the full-expression in the return statement.
(5.3) — A temporary bound to a reference in a new-initializer (5.3.4) persists until the completion of the
full-expression containing the new-initializer.
So two things. First, get_mhurg is undefined behavior. The lifetime of the temporary you're returning is not extended. Second, the temporary passed into id lasts until the end of the full-expression containing the function call, but no further. As with get_mhurg, the temporary is not through-extended. So that would also be undefined behavior.
My understanding and experimenting shows that the following is not
undefined behavior:
Well, your understanding and experimenting is wrong. Tip for the wise- don't experiment with undefined behaviour. For example, your experiment could randomly happen to indicate that it's well-defined behaviour, which is one of the possible outcomes of undefined behaviour.
Using the return value of get_mhurg() is undefined behaviour. You are accessing the temporary hurg() outside the scope in which it was created.
The id situation just goes to show that lifetime extension is fucking dumb.
Specifically, lifetime extension only applies to values directly. It never applies to references of any kind. You must initialize the reference with a value. id does not return a value, so no lifetime extension is applied.

Can a const int ref in a constructor safely bind to a literal?

I know the standard has an exception about extending the lifetime of temporaries that basically says binding a const reference in a constructor won't extend the lifetime, but does this also apply to literals? For example:
class C {
private:
const int& ref;
public:
C(const int& in)
: ref{in}
{ }
};
If I had a function returning an object of this type
C f() {
C c(2);
return c;
}
Would the value of c.ref be undefined in the caller if I know it's bound to a literal?
No. You will not be able to use the reference after the constructor finishes execution.
When a prvalue of non-class type is bound to a const-reference of that same type, a temporary is always introduced. Thus the constructor's parameter reference will refer to a temporary that is destroyed once the reference goes out of scope. After that happens, the member reference is dangling, and attempting to access the stored value behind that reference results in UB.
[dcl.init.ref]/5:
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 [..]
has a class type (i.e., T2 is a class type) [..]
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.
If the initializer expression
is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and [..]
has a class type [..]
Otherwise: (5.2.2.1)
If T1 is a class type [..]
If T1 is a non-class type, a temporary of type “cv1 T1” is created and copy-initialized (8.5) from the initializer expression.
The reference is then bound to the temporary.
And integer literals are, unsurprisingly, indeed prvalues. [expr.prim.general]/1:
A string literal is an lvalue; all other literals are prvalues.
Finally, in case this is unclear, [class.temporary]/5:
The temporary to which the reference is bound or the temporary that is
the complete object of a subobject to which the reference is bound
persists for the lifetime of the reference except:
A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits.
Short answer: Evaluating c.ref will almost certainly be illegal (invoke undefined behavior).
Long answer:
When binding a reference to an integer literal, what you are really doing is the following:
The integer literal refers to what is known as "a value that is not associated with an object".
To bind a reference to it, an object needs to be created that holds the same value. The reason for that is that a reference (or pointer) must always point to an object (which in turn is nothing more than a bit of memory). Therefore, a temporary object is created which holds the value.
Temporary objects are guaranteed to last as long as the expression they are created by is being evaluated. Since your object exists for longer, the temporary object that held your value is being destroyed early and the reference may not be accessed anymore.
Note that if you access c.ref within the expression that created c, you would actually be fine.

Will a reference bound to a function parameter prolong the lifetime of that temporary?

I have this code (simplified version):
const int& function( const int& param )
{
return param;
}
const int& reference = function( 10 );
//use reference
I can't quite decide to which extent C++03 Standard $12.2/5 wording
The temporary to which the reference is bound or the temporary that is the complete object to a subobject of which the temporary is bound persists for the lifetime of the reference...
is applicable here.
Is reference variable in the code above valid or dangling? Will the reference in the calling code prolong the lifetime of the temporary passed as the parameter?
A full-expression is an expression that is not a subexpression of another expression. In this case, the full-expression containing the call function( 10 ) is the assignment expression:
const int& reference = function( 10 );
In order to call function with the argument 10, a temporary const-reference object is created to the temporary integer object 10. The lifetime of the temporary integer and the temporary const-reference extend through the assignment, so although the assignment expression is valid, attempting to use the integer referenced by reference is Undefined Behavior as reference no longer references a live object.
The C++11 Standard, I think, clarifies the situation:
The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
...
— A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full-expression containing the call.
"The temporary to which the reference is bound ... persists for the lifetime of the reference". In this case, the lifetime of the reference ends at the end of the assignment expression, as does the lifetime of the temporary integer.
This will compile, but you will end up with a dangling reference. param is freed after function returns.
function is called with a reference to the temporary, anonymous object
function returns the reference
now that function has returned the temporary param is released
the reference is now dangling as the object was destroyed
If you had made it non-const, then it wouldn't have compiled because you cannot pass a non-const reference to an anonymous object.
From the C++11 viepoint a reference returned by a function is not a temporary:
12.12.1 Temporaries of class type are created in various contexts: binding a reference to a prvalue (8.5.3), returning
a prvalue (6.6.3), a conversion that creates a prvalue (4.1, 5.2.9, 5.2.11, 5.4), throwing an exception (15.1),
entering a handler (15.3), and in some initializations (8.5).
A function returning a reference dosn't return prvalue ("pure rvalue"), so it's not a temporary.
This seems quite natural: compiler can't manage lifetime of referenced objects, it is programmer's responsibility
Thus, the compiler doesn't provide any liftime guarantes for const int& reference since it is not bounded to temporary.
It is this part that is important
The temporary to which the reference is bound
In this case the parameter is bound to the temporary, and will be destroyed after the call.
You cannot extend the lifetime further by passing the reference on.