Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

lifetime of tempory generated variables

2 views
Skip to first unread message

mario semo

unread,
May 14, 2008, 10:56:23 AM5/14/08
to
Hello,

What does the C++ Norm says about the lifetime of compiler generated
temporary variables?

#include <stdio.h>

class BaseRef
{
//--------------------------------------------------------------------------
public:
//--------------------------------------------------------------------------

BaseRef()
{
printf("ctor BaseRef %p\n",this);
}

virtual ~BaseRef()
{
printf("dtor BaseRef %p\n",this);
}

void *qPtr()
{
return 0;
}
};

class Server
{
//--------------------------------------------------------------------------
public:
//--------------------------------------------------------------------------

Server()
{
}

virtual ~Server()
{
}

BaseRef queryBaseRef()
{
return BaseRef();
}

};

int main(int argc,char *argv[])
{
Server lServer;

printf("start\n");
{
// generates a temporary baseRef instance.
void * lPtr = lServer.queryBaseRef().qPtr();
printf("before }\n");
}
printf("after }\n");
printf("done\n");

return 0;
}


Here is the output of 2 different compilers:

VC9
start
ctor BaseRef 0012FF6C
dtor BaseRef 0012FF6C
before }
after }
done

VACPP
start
ctor BaseRef 12FF88
before }
dtor BaseRef 12FF88
after }
done

regards,

mario semo

gnuyuva

unread,
May 14, 2008, 11:45:53 AM5/14/08
to

The code generated by VC9 is more valid because it is guaranteed that
the lifetimes of the temporaries returned by value are valid till the
execution of the containing statement. So the temporary returned by
queryBaseRef is valid untill the qPtr() is evaluated. But the way the
code is generated by the compilers varies. GCC provides similar
results like VC9.

Marcel Müller

unread,
May 14, 2008, 5:18:21 PM5/14/08
to
mario semo schrieb:

> What does the C++ Norm says about the lifetime of compiler generated
> temporary variables?

Until the next statement.

> // generates a temporary baseRef instance.
> void * lPtr = lServer.queryBaseRef().qPtr();

lPtr is now a dangling reference and must mot be dereferenced anymore..

> printf("before }\n");
> }
> printf("after }\n");

> Here is the output of 2 different compilers:
>
> VC9
> start
> ctor BaseRef 0012FF6C
> dtor BaseRef 0012FF6C
> before }
> after }
> done

This is standard conform.

> VACPP
> start
> ctor BaseRef 12FF88
> before }
> dtor BaseRef 12FF88
> after }
> done

Here I am unsure.
As far as I know this is wrong, bcause the object must be destroyed
before the next statement. But maybe it /can/ be destroyed before the
next statement. Anyway it is undefined behaviour to rely in the object
existence after the statement has finished.
Maybe the automatic inliner understood your implicit dependency.

But you may explicitly extend the lifetime of temporaries creating a
const reference.

const BaseRef& tmp = lServer.queryBaseRef();

Now the temporary is valid until the end of the block. No copy is made.


Marcel

James Kanze

unread,
May 15, 2008, 4:10:13 AM5/15/08
to
On May 14, 11:18 pm, Marcel Müller <news.5.ma...@spamgourmet.com>
wrote:
> mario semo schrieb:

> > What does the C++ Norm says about the lifetime of compiler
> > generated temporary variables?

> Until the next statement.

Until the end of the full expression in which they were
generated, with a few exceptions. Thus, for example, in:

if ( generateATemporary ) ...

the temporary does not last until the end of the if
statement---only until the end of the full expression in the
condition.

All of the exceptions extend the lifetime in some way, so you're
guaranteed at least until the end of the full expression in
every case. The most important extention is in an
initialization expression---the lifetime is extended until the
object is fully initialized, e.g.:

MyClass object( generateATemporary ) ;

The full expression is just "generateATemporary", but the
temporary is guaranteed to last until we return from the
contructor of object (which is not part of the expression, but
implicit in the fact that this is a definition of a class type).

> > // generates a temporary baseRef instance.
> > void * lPtr = lServer.queryBaseRef().qPtr();

> lPtr is now a dangling reference and must mot be dereferenced
> anymore..

> > printf("before }\n");
> > }
> > printf("after }\n");
> > Here is the output of 2 different compilers:

> > VC9
> > start
> > ctor BaseRef 0012FF6C
> > dtor BaseRef 0012FF6C
> > before }
> > after }
> > done

> This is standard conform.
>
> > VACPP
> > start
> > ctor BaseRef 12FF88
> > before }
> > dtor BaseRef 12FF88
> > after }
> > done

> Here I am unsure.

It's not conform. The standard says exactly when the destructor
must be called. Before the standard, however, the ARM (and
earlier language specifications) were considerably looser: the
destructor could be called anytime after the temporary was
"used" and before the next closing brace. Thus (assuming s is
std::string, or something with a similar interface), given:

char const* f( char const* ps )
{
std::cout << ps << std::endl ;
return ps ;
}

int
main()
{
std::string a( "abc" ), b( "123" ) ;
if ( 1 ) {
char const* p = f( (a + b).c_str() ) ;
// Note the temporary in the
// call to f...
std::cout << p << std::endl ;
}
}

According to the standard, the output in f is fine (since the
temporary will remain until the end of the full expression, i.e.
the return from f). With some pre-standard compilers, however
(including all CFront based compilers, which many considered the
de facto standard), this code was perfectly fine. With others
(g++, for example), both output failed, since the call to
std::string::c_str() "used" the temporary, and it could be
immediately destructed (before the call to f).

In general, those compilers with a shorter than standard
lifetime (e.g. g++) changed very rapidly to the standard
lifetime; extending the lifetime typically wouldn't break much
code. Those which had a longer lifetime, however, tended to be
very cautious about shortening it, since the potential for
breaking code was much greater---even today, Sun CC has options
to support both lifetimes: standard, and until the next closing
brace. (I'm not sure off hand which is the default in the last
version, but at least until very, very recently, it was the
extended lifetime.)

> But you may explicitly extend the lifetime of temporaries
> creating a const reference.

> const BaseRef& tmp = lServer.queryBaseRef();

> Now the temporary is valid until the end of the block. No copy
> is made.

The temporary which is actually bound to tmp has its lifetime
extended, but other temporaries in the expression don't. And
whether a copy is made or not depends on the implementation (but
the object must be copiable, although as far as I know, only g++
enforces this correctly).

--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

0 new messages