Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Life time of an unnamed temporary constructed in if condition expression

Tags:

c++

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...)

like image 497
Adam Badura Avatar asked Nov 30 '12 10:11

Adam Badura


People also ask

How can you extend the lifetime of an object?

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.

What are temporary objects C++?

A temporary object is an unnamed object created by the compiler to store a temporary value.


2 Answers

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.

like image 164
Tino Didriksen Avatar answered Sep 19 '22 11:09

Tino Didriksen


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().

like image 41
Luc Touraille Avatar answered Sep 18 '22 11:09

Luc Touraille