|
Boost : |
From: Lois Goldthwaite (loisg_at_[hidden])
Date: 2000-10-11 08:43:40
András,
"Erdei, András" wrote:
>
> > Doesn't this 'optimization' conflict with the return value optimization?
>
> No. OTOH IMHO the original code may disallow return
> value optimization.
I must admit that I didn't review the original code, so there well may
be other stuff in there I don't know about. And in both versions that
we're talking about, the increment of the original value is only a side
effect, but an important one.
>
> > Specifically, by not using a named tmp to hold the value, but
> > constructing it directly in the return statement, aren't you telling the
> > compiler to optimize away the (local occurrence of) proxy's destructor?
>
> No.
>
> > Of course the destructor for the returned object will execute at _some_
> > later point, incrementing real at that time, but my personal opinion is
> > to opt for the certainty that the side effect of incrementing T _must_
> > happen by the conclusion of the ++ function.
>
> The opposite -- in conforming code you can't rely
> on the compiler doing the increment at any particular point
> during the evaluation of the expression. e.g.
>
> int i = 0 ;
> int j = i++ + i++ ; // j can be anything
But there is no sequence point in this example. There is a sequence
point at the beginning and end of each function. If you rewrote this as
int j = i.increment() + i.increment();
you couldn't know in what order the two calls would happen, but I
(personally) would expect that any side effects that happened _within_
the increment() function would be complete when it returned. This
doesn't apply to built-in types because the compiler might choose to
hold them in a register until the next sequence point.
I know both the C and C++ standards committees are looking for ways to
tighten up the specification of exactly what function points are and how
they affect code.
But to go back to the return value optimization question, aren't we
looking at two possible ways the compiler can generate the
[pseudo-]code? (loosely borrowing from Lippman's Inside the C++ Object
Model):
[1]
operator ++ ( proxy & __result, T& real, int )
{
__result::proxy( real ); // direct ctor into return value
}
__result::~proxy(); // happens later -- increment real now
[2]
operator ++ ( proxy & __result, T& real, int )
{
proxy __tmp( real ); // construct local temporary
__result::proxy( __tmp ); // copy ctor into return value
__tmp::~proxy(); // destroy local -- increment real here
}
__result::~proxy(); // happens later -- increment real again?
If I understand as much (little?) as I think I do, the statement
return proxy( t );
guarantees that option 1 is what the compiler will use. But can we be
sure that the __result destructor will be complete by the sequence point
which immediately follows this function call? Or might it hang around
until the sequence point at the next semicolon?
This is an clever technique, but I'd be afraid the code might decay
under maintenance. I feel that's less of a danger with the slow-but-sure
implementation you suggest replacing.
Cheers,
Lois
// original code, to save looking back over messages:
> class proxy
> {
> public :
> operator T const & ( void ) { return real ; }
> ~proxy() { if ( ! std::uncaught_exception() ) ++real ; } ;
> private :
> friend proxy operator++( T & , int ) ;
> proxy( T & real ) : real( real ) {} ;
> T & real ;
> } ;
>
> proxy
> operator++( T & t , int )
> {
> return proxy( t ) ;
> }
>
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk