 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Maciej Sobczak Guest
|
Posted: Tue Dec 16, 2003 11:28 am Post subject: Returned object: construction order guarantee |
|
|
Hi,
Consider:
class A { ... };
class B { ... };
B fun()
{
A a;
// ...
return b; // b is either B or convertible to B
}
int main()
{
B b = fun();
}
I would like to know (ie. track down in the standard) the guarantee that
the returned object will be created before the local 'a' gets destructed.
The 6.6.3 is not very constraining and adding the possibility of RVO I
came to the conclusion that the returned object can be created (or
converted) *after* the destruction of local 'a' object and stay conformant.
Consider also:
struct C
{
B getB()
{
A a;
return b;
}
private:
B b;
};
int main()
{
C c;
B b = c.getB();
}
The question is similar: is it guaranteed that the copy of c.b will be
performed before local object 'a' gets destroyed? Is it possible to
influence it (RVO, conversions, etc.)?
In case you wonder why I care: class A is a RAII lock on mutex.
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Dhruv Guest
|
Posted: Tue Dec 16, 2003 6:24 pm Post subject: Re: Returned object: construction order guarantee |
|
|
On Tue, 16 Dec 2003 06:28:02 -0500, Maciej Sobczak wrote:
| Quote: | Hi,
Consider:
class A { ... };
class B { ... };
B fun()
{
A a;
// ...
return b; // b is either B or convertible to B
}
int main()
{
B b = fun();
}
I would like to know (ie. track down in the standard) the guarantee that
the returned object will be created before the local 'a' gets destructed.
The 6.6.3 is not very constraining and adding the possibility of RVO I
came to the conclusion that the returned object can be created (or
converted) *after* the destruction of local 'a' object and stay conformant.
|
I do not know about standard conformance, but on my implementation, the
return value is created first, and then the locals are destroyed.
| Quote: | Consider also:
struct C
{
B getB()
{
A a;
return b;
}
private:
B b;
};
int main()
{
C c;
B b = c.getB();
}
The question is similar: is it guaranteed that the copy of c.b will be
performed before local object 'a' gets destroyed? Is it possible to
influence it (RVO, conversions, etc.)?
|
RVO cannot be performed here, because you are returning a named variable,
and to top it, it isn't even local, so any chances of RVO happening are
lost, the instant the variable is not local to the function.
| Quote: |
In case you wonder why I care: class A is a RAII lock on mutex.
|
I should expect the lock to remain active till the local return value is
created, but *NOT* till the return value gets copied to the destination.
SO, the lock is released before the temporary created form the function
return is copied to it's destination.
Regards,
-Dhruv.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
John Potter Guest
|
Posted: Tue Dec 16, 2003 9:18 pm Post subject: Re: Returned object: construction order guarantee |
|
|
On 16 Dec 2003 13:24:10 -0500, "Dhruv" <dhruvbird (AT) gmx (DOT) net> wrote:
| Quote: | On Tue, 16 Dec 2003 06:28:02 -0500, Maciej Sobczak wrote:
struct C
{
B getB()
{
A a;
return b;
}
private:
B b;
};
int main()
{
C c;
B b = c.getB();
}
The question is similar: is it guaranteed that the copy of c.b will be
performed before local object 'a' gets destroyed? Is it possible to
influence it (RVO, conversions, etc.)?
RVO cannot be performed here, because you are returning a named variable,
and to top it, it isn't even local, so any chances of RVO happening are
lost, the instant the variable is not local to the function.
|
You have a false definition of RVO. The optimization is visible at the
caller. The implementation functions as-if
void C::getB (void* p) {
A a;
new(p)B(b);
}
int main () {
C c;
char __unnamed[sizeof B];
getB(__unnamed);
B& b(reinterpret_cast<B&>(__unnamed[0]));
}
The return value of the function was removed by constructing the callers
B object in the function.
Addressing the original question, I don't think there is any problem
with the destruction of the A object, but there could be another
problem.
B C::getB () {
B result;
A a;
// compute result from b
return result;
}
With NRVO, result will be removed and the B in main will be default
constructed before a is constructed. I think the sequence point
after defining a in the original prevents moving the construction
of b ahead of it.
1.9/17 seems to relate to the original question. It would seem that
the sequence point at the end of the return statement preceeds the
actual code required to destroy locals and exit the function.
3.7.2 covers the storage duration for the objects and effects the
lifetime.
6.7 also applies.
I think the answer is that the return statement copies the return value
out of the function to where ever and then block exit code executes.
If the lock object is trying to cover all copies of B objects, it
will not do the job because there could be additional copies of the
return value in the expression that called the function. If it is
only intended to protect the member b, it seems fine.
John
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Wed Dec 17, 2003 7:07 pm Post subject: Re: Returned object: construction order guarantee |
|
|
John Potter <jpotter (AT) falcon (DOT) lhup.edu> wrote
[...]
| Quote: | Addressing the original question, I don't think there is any problem
with the destruction of the A object, but there could be another
problem.
B C::getB () {
B result;
A a;
// compute result from b
return result;
}
With NRVO, result will be removed and the B in main will be default
constructed before a is constructed. I think the sequence point
after defining a in the original prevents moving the construction
of b ahead of it.
|
I'm not sure if I understand what you are saying. Suppose I write:
B getB()
{
Locker a( resourceManagementLock ) ;
B result ;
return result ;
}
Now suppose I use it in an expression like:
B ab( getB() ) ;
Does NRVO allow the default construction of a B before I have
constructed the lock a?
--
James Kanze GABI Software mailto:kanze (AT) gabi-soft (DOT) fr
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Dhruv Guest
|
Posted: Wed Dec 17, 2003 7:12 pm Post subject: Re: Returned object: construction order guarantee |
|
|
On Tue, 16 Dec 2003 16:18:11 -0500, John Potter wrote:
[...]
| Quote: |
RVO cannot be performed here, because you are returning a named variable,
and to top it, it isn't even local, so any chances of RVO happening are
lost, the instant the variable is not local to the function.
You have a false definition of RVO. The optimization is visible at the
caller. The implementation functions as-if
|
Yes, now all that misconception is clear *hopefully*.
| Quote: | Addressing the original question, I don't think there is any problem
with the destruction of the A object, but there could be another
problem.
B C::getB () {
B result;
A a;
// compute result from b
return result;
}
With NRVO, result will be removed and the B in main will be default
constructed before a is constructed. I think the sequence point
after defining a in the original prevents moving the construction
of b ahead of it.
1.9/17 seems to relate to the original question. It would seem that
the sequence point at the end of the return statement preceeds the
actual code required to destroy locals and exit the function.
3.7.2 covers the storage duration for the objects and effects the
lifetime.
6.7 also applies.
I think the answer is that the return statement copies the return value
out of the function to where ever and then block exit code executes.
|
I have one more question:
Consider:
A foo ()
{
Lock X;
A return_value;
//Some more stuff.
return return_value;
}
Now, a compiler implementing NRVO will have to destroy lock before
return_value is destroyed. So, is it legal for the order of destruction of
locals to be not the exact reverse of the order of their construction? I
could not find this guarantee anywhere in the standard.
Regards,
-Dhruv.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
John Potter Guest
|
Posted: Thu Dec 18, 2003 8:49 am Post subject: Re: Returned object: construction order guarantee |
|
|
On 17 Dec 2003 14:07:55 -0500, [email]kanze (AT) gabi-soft (DOT) fr[/email] wrote:
| Quote: | John Potter <jpotter (AT) falcon (DOT) lhup.edu> wrote in message
news:<jtkutvcst3gvhb5ktt205c2nebuqc2lkaf (AT) 4ax (DOT) com>...
Addressing the original question, I don't think there is any problem
with the destruction of the A object, but there could be another
problem.
B C::getB () {
B result;
A a;
// compute result from b
return result;
}
With NRVO, result will be removed and the B in main will be default
constructed before a is constructed. I think the sequence point
after defining a in the original prevents moving the construction
of b ahead of it.
I'm not sure if I understand what you are saying. Suppose I write:
B getB()
{
Locker a( resourceManagementLock ) ;
B result ;
return result ;
}
Now suppose I use it in an expression like:
B ab( getB() ) ;
Does NRVO allow the default construction of a B before I have
constructed the lock a?
|
I tried to say above that I think the sequence point prevents
reordering.
construct a
construct result
copy construct return value from result
destruct a
copy construct ab from return value
Both copies can be removed and ab will be constructed while the
lock is active. Could that be a problem? I don't know what the
lock is protecting.
John
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
John Potter Guest
|
Posted: Thu Dec 18, 2003 8:54 am Post subject: Re: Returned object: construction order guarantee |
|
|
On 17 Dec 2003 14:12:41 -0500, "Dhruv" <dhruvbird (AT) gmx (DOT) net> wrote:
| Quote: | I have one more question:
Consider:
A foo ()
{
Lock X;
A return_value;
//Some more stuff.
return return_value;
}
Now, a compiler implementing NRVO will have to destroy lock before
return_value is destroyed. So, is it legal for the order of destruction of
locals to be not the exact reverse of the order of their construction? I
could not find this guarantee anywhere in the standard.
|
That's because it is not allowed. However, there is no need to destroy
something which is not constructed. Yes, this is messing with object
lifetime and there were some heated debates in csc++ when this was
standardized.
Let's finish the example by using it.
A a = foo();
construct X
construct return_value
copy construct unnamed from return_value
destruct return_value
destruct X
copy construct a from unnamed
destruct unnamed
We already have the lifetimes of X and unnamed not nested. That is
acceptable because they are not in the same block. If we consider
the storage duration not the lifetime, they are nested.
Now we apply RVO to remove unnamed.
construct X
construct return_value
copy construct a from return_value
destruct return_value
destruct X
We just shifted the not nested from unnamed to a. Now NRVO.
construct X
construct a
destruct X
We now removed return_value and there is no local to be destructed
before X. The strange part is that the lifetime of the non-local a
starts within foo. If these were fundamental types, lifetime and
storage duration would be the same. There would also be no optimization
done because the unnamed would be an rvalue in a register not an
object. The abstract machine follows the same basic idea for rvalues
which are objects. These optimizations are about real code on real
machines and do mess with the model. This is the special case which
is exempted from the as-if rule. It is the only optimization which
can be detected by a conforming program.
Historical note: NRVO was enabled in cfront by a user defined copy
ctor. If the compiler generated copy ctor was used there was no NRVO.
An interesting optimization switch.
John
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Dhruv Guest
|
Posted: Thu Dec 18, 2003 4:56 pm Post subject: Re: Returned object: construction order guarantee |
|
|
On Thu, 18 Dec 2003 03:54:42 -0500, John Potter wrote:
[...]
| Quote: |
Now, a compiler implementing NRVO will have to destroy lock before
return_value is destroyed. So, is it legal for the order of destruction of
locals to be not the exact reverse of the order of their construction? I
could not find this guarantee anywhere in the standard.
That's because it is not allowed. However, there is no need to destroy
something which is not constructed. Yes, this is messing with object
lifetime and there were some heated debates in csc++ when this was
standardized.
Let's finish the example by using it.
A a = foo();
construct X
construct return_value
copy construct unnamed from return_value
destruct return_value
destruct X
copy construct a from unnamed
destruct unnamed
|
Agreed.
| Quote: | We already have the lifetimes of X and unnamed not nested. That is
acceptable because they are not in the same block. If we consider
the storage duration not the lifetime, they are nested.
Now we apply RVO to remove unnamed.
construct X
construct return_value
copy construct a from return_value
destruct return_value
destruct X
|
Agreed again.
| Quote: | We just shifted the not nested from unnamed to a. Now NRVO.
construct X
construct a
destruct X
|
And again.
| Quote: | We now removed return_value and there is no local to be destructed
before X. The strange part is that the lifetime of the non-local a
starts within foo. If these were fundamental types, lifetime and
storage duration would be the same. There would also be no optimization
done because the unnamed would be an rvalue in a register not an
object. The abstract machine follows the same basic idea for rvalues
which are objects. These optimizations are about real code on real
machines and do mess with the model. This is the special case which
is exempted from the as-if rule. It is the only optimization which
can be detected by a conforming program.
|
However, check out what g++3.2 does for these cases:
I use my previously mentioned verbose class:
verbose foo ()
{
verbose ret (11);
verbose v (23);
return v;
} //NRVO
//CASE-1:
int main ()
{
verbose v = foo (); //Initialization.
cout<<"After call"<
}
//Output for case-1:
verbose::verbose (11)
verbose::verbose (23)
verbose::~verbose () : 11
After call
verbose::~verbose () : 23
Agreed.
//Case-2:
int main ()
{
verbose v(45);
v = foo (); //Assignment.
cout<<"After call"<
}
//Output for Case-2:
verbose::verbose (45) <- v(45)
verbose::verbose (11) <- inside foo
verbose::verbose (23) <- inside foo
verbose::~verbose () : 11 <- ???
verbose::operator= () 45 = 23 <- Still more ???
verbose::~verbose () : 23 <- and he faints?
After call
verbose::~verbose () : 23
| Quote: | Historical note: NRVO was enabled in cfront by a user defined copy
ctor. If the compiler generated copy ctor was used there was no NRVO.
An interesting optimization switch.
|
Any particaulr property being exploited apart from the fact that the
author assumed that classes with trivial copy ctor's did not do any heavy
copying?
Regards,
-Dhruv.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
John Potter Guest
|
Posted: Fri Dec 19, 2003 7:43 am Post subject: Re: Returned object: construction order guarantee |
|
|
On 18 Dec 2003 11:56:24 -0500, "Dhruv" <dhruvbird (AT) gmx (DOT) net> wrote:
| Quote: | On Thu, 18 Dec 2003 03:54:42 -0500, John Potter wrote:
However, check out what g++3.2 does for these cases:
I use my previously mentioned verbose class:
verbose foo ()
{
verbose ret (11);
verbose v (23);
return v;
} //NRVO
//Case-2:
int main ()
{
verbose v(45);
v = foo (); //Assignment.
cout<<"After call"<
}
//Output for Case-2:
verbose::verbose (45) <- v(45)
verbose::verbose (11) <- inside foo
verbose::verbose (23) <- inside foo
|
Consturcting a temporary in main's space.
| Quote: | verbose::~verbose () : 11 <- ???
verbose::operator= () 45 = 23 <- Still more ???
verbose::~verbose () : 23 <- and he faints?
|
When main destroys it.
| Quote: | After call
verbose::~verbose () : 23
|
Watch the mirrors. The compiler is faster than the eye.
int main () {
verbose v(45);
{
verbose const& r(foo()); // space here ctor there
v = r;
} // the temporary gets destroyed here.
cout << "After call" << endl;
}
That may still be a bit fast. Let's try it in slow motion.
verbose& trueFoo (void* raw) {
verbose ret(11);
return *new(raw)verbose(23); // construct here in space there
}
int main ()
{
verbose v(45);
{
char raw[sizeof(verbose)]; // the space exists
verbose& tmp(trueFoo(raw)); // the object exists
v = tmp;
tmp.~verbose(); // the object dies
} // and the space goes away
cout<<"After call"<
}
Both version compile and give the same results using g++ on cygwin.
| Quote: | Historical note: NRVO was enabled in cfront by a user defined copy
ctor. If the compiler generated copy ctor was used there was no NRVO.
An interesting optimization switch.
Any particaulr property being exploited apart from the fact that the
author assumed that classes with trivial copy ctor's did not do any heavy
copying?
|
Nope, just amusing to use code as a compiler switch.
struct S { double data[10000]; };
Hard to copy but no NRVO.
John
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Dhruv Guest
|
Posted: Sat Dec 20, 2003 7:56 pm Post subject: Re: Returned object: construction order guarantee |
|
|
On Fri, 19 Dec 2003 02:43:21 -0500, John Potter wrote:
[...]
| Quote: | verbose foo ()
{
verbose ret (11);
verbose v (23);
return v;
} //NRVO
//Case-2:
int main ()
{
verbose v(45);
v = foo (); //Assignment.
cout<<"After call"<
}
//Output for Case-2:
verbose::verbose (45) <- v(45)
verbose::verbose (11) <- inside foo
verbose::verbose (23) <- inside foo
Consturcting a temporary in main's space.
|
Is the compiler permiitted to construct locals (that will not be returnred
or anything) in main's space?
| Quote: | verbose::~verbose () : 11 <- ???
verbose::operator= () 45 = 23 <- Still more ???
verbose::~verbose () : 23 <- and he faints?
When main destroys it.
After call
verbose::~verbose () : 23
Watch the mirrors. The compiler is faster than the eye.
|
I didn't quite get you.
| Quote: | Both version compile and give the same results using g++ on cygwin.
|
I see what you're trying to say. The space is in main, while the actual
ctor call is made from foo().
[...]
| Quote: | Nope, just amusing to use code as a compiler switch.
struct S { double data[10000]; };
Hard to copy but no NRVO.
|
BTW, when you have a struct like the above, does C++ copy each member, or
each byte of the array in the default compiler generated copy ctor?
Regards,
-Dhruv.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Mon Dec 22, 2003 7:20 pm Post subject: Re: Returned object: construction order guarantee |
|
|
"Dhruv" <dhruvbird (AT) gmx (DOT) net> wrote
| Quote: | On Fri, 19 Dec 2003 02:43:21 -0500, John Potter wrote:
[...]
verbose foo ()
{
verbose ret (11);
verbose v (23);
return v;
} //NRVO
//Case-2:
int main ()
{
verbose v(45);
v = foo (); //Assignment.
cout<<"After call"<
}
//Output for Case-2:
verbose::verbose (45) <- v(45)
verbose::verbose (11) <- inside foo
verbose::verbose (23) <- inside foo
Consturcting a temporary in main's space.
Is the compiler permiitted to construct locals (that will not be
returnred or anything) in main's space?
|
Can a conforming program tell?
As far as the standard is concerned, there is no such thing as "main's
space". The compiler can put free variables where ever it wants,
provided the semantics of the code are obeyed. Or, as in this case, it
has a special license to pretend that the semantics are other than what
they are (e.g. that the copy constructor and the destructor have no
visible side effects, even if they actually do).
John's use of the expression "main's space" was purely pedegogic, a
simple way of explaining one particular implementation technique in an
intuitive (and not too formal) fashion.
| Quote: | [...]
Nope, just amusing to use code as a compiler switch.
struct S { double data[10000]; };
Hard to copy but no NRVO.
BTW, when you have a struct like the above, does C++ copy each member,
or each byte of the array in the default compiler generated copy ctor?
|
Each member. In the above case, the compiler is likely to realize that
a bitwise copy will have the same effect as copying each member, and use
it, but formally, that is an optimization.
--
James Kanze GABI Software mailto:kanze (AT) gabi-soft (DOT) fr
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|