How Standard defines the life time of a temporary object constructed during evaluation of an if
condition expression?
I looked for this information and found something similar in an example to point [10] in $1.9, page 10. (I'm referring here to Final Draft of the new specification.) Yet still it wasn't clear (enough) for me and since Visual C++ acted differently than my understanding of that example I decided to ask.
Please provide proper references to specification.
If you name the object it persists for the entire if
(so both true
block and false
block but is destroyed before if
ends).
For example:
if ( MyClass x = f() ) { /* ... */ } else { /* ... */ } nextInstruction();
x
can be used in both if
blocks but gets destroyed before nextInstruction
gets called.
But what if you don't name it?
if ( f() ) { /* ... */ } else { /* ... */ } nextInstruction();
In my understanding of the referenced part of specification the value returned by f()
will be destroyed before execution enters one of the blocks (either for true
or for false
).
However Visual C++ destroys that temporary object as if it was named. (EDIT: as Tino Didriksen pointed out Visual C++ does work well here. And indeed now I confirm that as well. I must have made a mistake when looking at initial tests results!)
This does matter in some edge cases (lets not discuss here how likely they are or whether it is good to write code that way...).
For example lets have:
class ScopedLock { public: ~ScopedLock() { if ( isLocked() ) unlock(); } operator bool() const { return isLocked(); } /* ... */ };
Now if we have code like:
if ( ScopedLock lock = resource.lock() ) { /* ... */ }
we can be sure that when execution enters the true
block we own the resource and that it will not be unlocked before we leave that block.
But what if someone wrote it like this:
if ( resource.lock() ) { /* ... */ }
Now it is crucial at which point the destructor for the temporary ScopedLock
will be called. Because it determines whether this code is correct or not (in the sens of resource usage). (Again lets skip discussing whether writing such code is bad in general. That is not the point of this question...)
The lifetime of a temporary object may be extended by binding to a const lvalue reference or to an rvalue reference (since C++11), see reference initialization for details.
A temporary object is an unnamed object created by the compiler to store a temporary value.
However Visual C++ destroys that temporary object as if it was named.
No it doesn't...
Given the code
#include <iostream> struct S { S() { std::cout << "S()" << std::endl; } S(const S&) { std::cout << "S(const S&)" << std::endl; } ~S() { std::cout << "~S()" << std::endl; } operator bool() const { return true; } }; int main() { std::cout << "main 1" << std::endl; if (S s = S()) { std::cout << "if 1" << std::endl; } else { std::cout << "else 1" << std::endl; } std::cout << "main 2" << std::endl; if (S()) { std::cout << "if 2" << std::endl; } else { std::cout << "else 2" << std::endl; } std::cout << "main 3" << std::endl; }
GNU g++ 4.5.1 and g++ 4.7.0 and VC++ 2010 and VC++ 2012 have the exact same output:
main 1 S() if 1 ~S() main 2 S() ~S() if 2 main 3
where the named temporary is destroyed after the if/else and the unnamed temporary is destroyed at the end of the condition.
As far as I can tell, Visual C++ is wrong in this regard.
Indeed, a temporary is (almost always) destroyed at the end of the full expression in which it is created:
§12.2/3: [...]Temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created
Looking at the definition of a selection statement (if
and switch
), we can see that the condition is an expression:
§6.4: selection-statement: if ( condition ) statement if ( condition) statement else statement switch ( condition ) statement condition: expression type-specifier-seq declarator = assignment-expression
So any temporaries created in the condition should be destroyed before executing the following statement (unless bound to a reference-to-const).
The behavior when introducing a new name in the condition is specified in §6.4/3:
A name introduced by a declaration in a condition [...] is in scope from its point of declaration until the end of the substatements controlled by the condition.
So in your example, x
is in scope in the two branches of the if
, and destroyed before evaluating nextInstruction()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With