Section 12.2.5 of the standard says:
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. A temporary bound to the returned value in a function return
statement (6.6.3) persists until the function exits. In all these
cases, the temporaries created during the evaluation of the expression
initializing the reference, except the temporary to which the
reference is bound, are destroyed at the end of the full-expression in
which they are created and in the reverse order of the completion of
their construction.
I'm trying to understand the following code:
#include <iostream>
const int& foo(const int& fooRef)
{
return fooRef;
} // #0
int main (void)
{
const int& numberRef = foo(5); // #1
std::cout << numberRef; // #2
return 0;
}
On line #1 a temporary object is created and bound to fooRef. fooRef is destroyed on line #0. I thought the temporary should be destroyed here since lifetime-extension is not transitive.
Questions:
What does until the function exits mean? Does it mean untill it finished executing?
Why do I get a 5 output. Does a temporary object still exist on line #2?
How can I interpret the standard quote to figure out how this example works?
Step-by-step atomic walk-through with references to the standard would be greatly appreciated. Thank you!
P. S. An accepted answer here also told the the code is broken and I do not get, why I get such output of program.
What does until the function exits mean? Does it mean untill it finished executing?
Yes.
Why do I get a 5 output. Does a temporary object still exist on line #2?
Dereferencing a reference which is not bound to a living object is undefined behavior, so you may get 5 as well as 42 as well as anything else (including a crash). You simply cannot have any expectation on a program that has undefined behavior.
How can I interpret the standard quote to figure out how this example works?
Pretty much like you did already.The temporary gets bound to the function parameter fooRef, which gets destroyed when returning from the function. Since that temporary is bound to the returned value, that object ceases to exist when the function returns. Later on, you are dereferencing a dangling reference, which gives you UB.
It means until the closing brace, i.e. }.
You invoked UB, you have a dangling reference.
Try the following modification of your code and see what it prints. It probably will print 6 because that is what was last on the stack. Or try passing a std::string instead, you might get a crash.
int main (void)
{
const int& numberRef = foo(5);
foo(6);
std::cout << numberRef;
return 0;
}
Related
In the following example:
http://coliru.stacked-crooked.com/a/7a1df22bb73f6030
struct D{
int i;
auto test2(int&& j){
return [&](){ // captured by reference!
cout << i*(j);
};
}
};
int main()
{
D d{10};
{
auto fn = d.test2(10);
fn(); // 1. wrong result here
d.test2(10)(); // 2. but ok here
}
}
Why does d.test2(10)(); work?
Should it really work, or thats just my undefined behavior equals correct result?
P.S. After reading this I see only one explanation: in (2) temporary lifetime prolongs till the end of the expression, and call happens in the same expression with && crteation; while (1) actually consists from 2 expressions:
a temporary bound to a reference parameter in a function call exists
until the end of the full expression containing that function call: if
the function returns a reference, which outlives the full expression,
it becomes a dangling reference.
Is this the case?
A temporary object lasts until the end of the line (well, full expression) where it is created, unless the lifetime is extended.
Your code does not extend the lifetimes of any temporaries. Lifetime extension through binding to references does not "commute", only the first binding extends lifetime.
So the furst case is UB as you have a dangling reference. The referred to temporary goes away st the end of the line: on the next line uou follow the reference, and chaos hapens.
In the second case, your reference does not extend the lifetime of the temporary, but the temporary lasts longer than the reference that binds to it does! They both die at the end of the line, in reverse order of construction.
So the call works.
Should it really work, or thats just my undefined behavior equals correct result?
Seems like it. In the example you linked, you have these warnings:
warning: '<anonymous>' is used uninitialized in this function [-Wuninitialized]
Uninitialized objects have indetermine values, and trying to access those values results in undefined behavior.
Only for curiosity and educating and clarification reasons I would like to ask that the way I use references and values are good practices or not.
Theoretically:
class ComplexGraphicalShape {
...
public:
void setRasterImageURL(const QString &rasterImageURL);
const QString &rasterImageURL() const;
...
private:
const QString *_rasterImageURL;
};
...
void ShadowGram::setRasterImageURL(const QString &rasterImageURL) {
safeDelete(_rasterImageURL); // handle deletion
_rasterImageURL = new QString(rasterImageURL);
}
const QString &ShadowGram::rasterImageURL() const{
// Question 2: Why is it a problem if I return
// return "www.url.com/shape_url.jpg"
return *_rasterImageURL; // that is the right way
}
...
complexGraphicalShape().setRasterImageURL(kURLImagesToShare + imageName);
complexGraphicalShape().setRasterImageURL("www.url.com/url.jpg"); // Question 1.
My first question is that how long can I use the temporary object reference which is created inside setRasterImageURL functioncall? Where exist that variable?(in the stack If I am not mistaken, but what if I call another function with that temporary reference.
My second question is that why I got a warning in Question 2 section if I would like to use this return "www.url.com/shape_url.jpg"? That thing is kind of similar. How long can I use that temporary object?
Thanks for your time for the answer and explanations
The temporary exists until setRasterImageURL returns, so you can safely pass a reference to it along, but you need to be careful not to save the reference for later. The temporary is stored wherever the compiler wants to. The reference is most likely passed either in a register or on the stack.
It is a problem because you're returning a reference to a temporary QString object, and that object is destroyed when the function returns. You're not allowed to use the reference at all.
Passing a reference "inwards" to a function is (usually) safe as long as you don't store it, while passing a reference "outwards" from a function requires you to make sure that the referenced object still exists when the function returns.
Q1: The temporary string exists as long as the temporary reference that is "bound" to it. That is - as long as you are "inside" setRasterImageURL() function. This - of course - includes all functions called "within" this function. Note that storing another reference to this temporary string does NOT prolong the lifetime of the temporary object.
complexGraphicalShape().setRasterImageURL("www.url.com/url.jpg");
// the temporary object is "destroyed" when it goes out of scope, and it's scope is just the called function
Q2: The problem with returning is that you use "C string" (array of characters) to create a temporary QString object (on stack, still inside the function) and return reference to that temporary. As this temporary object is destroyed right after this function returns, your reference is never valid and refers to a dead object. On the other hand - returning a reference to a member variable works, because this object is not destroyed, so the reference is valid as long as your main object lives.
const QString &ShadowGram::rasterImageURL() const{
return "www.url.com/shape_url.jpg"
// the temporary object is destroyed here, before the function returns, reference is invalid
}
My first question is that how long can I use the temporary object reference which is created inside setRasterImageURL functioncall?
It's not created inside the function call, it's created on the caller's stack before the function is called, and is destroyed after the function returns.
Where exist that variable?(in the stack If I am not mistaken, but what if I call another function with that temporary reference.
Yes, on the stack. It is destroyed at the ; after the function call returns (at the end of the "full expression").
That thing is kind of similar. How long can I use that temporary object?
Until the end of the full expression that creates the temporary, which is the return statement, so it goes out of scope immediately before the function has even finished returning. That's why you get a warning - the returned reference is bound to an object which no longer exists, and is never safe to use.
Both these cases are covered by 12.2 [class.temporary] paragraph 5 in the standard:
— 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.
— 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.
I wonder if the void func(const char *str); refer to a valid str if I wrote as follow:
auto str = string("hello").c_str();
func(str);
How is it different from code below?
func(string("hello").c_str())
In both cases, the string object is a temporary, destroyed at the end of the statement.
In the first case, str ends up dangling - pointing to memory that was managed by the temporary string, but which has now been destroyed. Doing anything with it is an error, giving undefined behaviour.
In the second case, the temporary string is not destroyed until after the function returns. So this is fine, as long as the function doesn't keep hold of the pointer for something else to use later.
The difference is that the first creates a temporary string object that gets destroyed at the end of the first statement, so str becomes a dangling pointer. The second also creates a temporary, but it exists throughout the call to func because the temporary object doesn't get destroyed until after the call to func returns.
From Paragraph 12.2/3 of the C++11 Standard:
[...] Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. This is true even if that evaluation ends in throwing an exception. [...]
This means that the temporary created within the expression that contains the call to func() will live until function call returns.
On the other hand, the lifetime of the temporary in the first code snippet will end up before func() is invoked, and str will be dangling. This will result in Undefined Behavior.
void foo(const Object & o = Object()) {
return;
}
In the function above, when is ~Object supposed to be called ? when the function exit or when at the end of the block surrounding the call site ?
The default argument will be destroyed at the end of the complete expression that contains the function call.
To elaborate a bit on what David said, the standard says in section 12.2 [class.temporary]:
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 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.
...
So they are neither destroyed when the function exits nor when the block containing the call ends, but at the end of the complete statement that contains the function call (simply said, at the first semicolon after the function call, in the calling context).
EDIT: So say we got:
int foo(const Object & o = Object());
some_stuff();
std::cout << (foo() + 7);
other_stuff();
This sould be roughly equivalent to the following (mind the conceptual scope block):
some_stuff();
{
Object o; // create temprorary
int i = foo(o); // and use it
int j = i + 7; // do other things
std::cout << j; // while o still alive
} // finally destroy o
other_stuff();
EDIT: As pointed out by Michael in his comment, this "statement/semicolon"-analogy I gave is rather a simplification of the term "full-expression" and there are cases where it is a bit different, like his example:
if(foo()) bar();
Which would destroy the temporary before bar is called and thus be different from the expression statement:
foo() ? bar() : 0;
But nevertheless, the "semicolon"-analogy is often a good fit, even if a full-expression is not neccessarily the same as a statement (which can consist of multiple full-expressions).
I don't think this code should compile. You can't bind a reference to a temporary unless it's const. And if it was const the temporary should be kept alive until the end of the function expression. Just the same as a local variable defined within it.
What happens to the reference in function parameter, if it gets destroyed when the function returns, then how const int *i is still a valid pointer?
const int* func(const int &x = 5)
{
return &x;
}
int main()
{
const int *i = func();
}
§12.2/5:
"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."
That means as i is being initialized, it's getting the address of a temporary object that does exist at that point. As soon as i is initialized, however, the temporary object will be destroyed, and i will become just another dangling pointer.
As such, yes, the function is valid -- but with the surrounding code as you've written it, any code you added afterward that attempted to dereference i would give undefined behavior.
Just because a pointer has a value doesn't mean it's a valid pointer.
In this case it holds an address which used to be that of x, and chances are that address still has the value 5, but it's not valid pointer and you can't count on that value being there.
int i points to a patch of memory that is unsafe to access, it is not a valid pointer.
the variable "i" is still a pointer, but even reading the value it points to will give you undefined behavior. That's why you should never write a function like func.
I think that x is created as an un-named temporary on the stack in setting up the call to func(). This temporary will exist until at least the end of the statement in the caller. So the int* i is perfectly valid. It only ceases to be valid at the end of the statement - which means that you cannot use it.
There is something in the standard about un-named temporaries being retained until the last reference to them goes out of scope, but I don't think it covers this explicit and hidden indirection.
[ Happy to have someone tell me otherwise.]
5 is program data. It is in the data segment, not the stack or heap.
So a pointer or reference to it will remain valid for the duration of the program.
Default arguments are evaluated every time the function is called, so the call func() is actually func(5) which is binding a temporary to a reference-to-const. The lifetime of that temporary is then extended till the end of the function and the object is destroyed. Any pointer to this object after that is invalid and dereferencing it is undefined behaviour.