lifetime of tempory generated variables

M

mario semo

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
 
G

gnuyuva

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

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

Marcel Müller

mario said:
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
 
J

James Kanze

mario semo schrieb:
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).
lPtr is now a dangling reference and must mot be dereferenced
anymore..
This is standard conform.
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).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top