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

More side effects of copy elision

94 views
Skip to first unread message

Andrzej Krzemieński

unread,
Jan 15, 2012, 6:15:09 PM1/15/12
to
Hi,
The current standard says (12.8, p. 31 and 32) that the copy/move from
an automatic object into a temporary returned by a function as well as
the copy/move from a temporary to an automatic object can be elided,
thus avoiding any copying/moving in the following example.

vecotr<string> generate()
{
vecotr<string> ans;
populate(ans);
return ans;
}

auto vec = generate();

The standard also says that this optimization is even allowed if copy/
move ctor or destructor have side effects. This somehow implies that
it is only constructor's or destructor's side effects that can affect
program's behaviour if elided. But how about the following case, if I
slightly change function generate():

vecotr<string> generate()
{
vecotr<string> ans;
populate(ans);
ScopeGuard g = [&]{ ans.clear(); }
return ans;
}

ScopeGuard woks such that it registers a callback in the constructor,
and calls the callback in the destructor. If copy elision is not
performed, the additional call to function clear() has no impact on
the program, because we are working on a local copy. However, if the
copies are elided, the auto variable 'ans' inside the function, and a
temporary, and the initialized global variable 'vec' are to be treated
as the same object. In this case, if I interpret the standard
correctly, the value of global 'vec' will be cleared. This would not
have been the case if the moves were not elided. Thus, copy elision
may change program behaviour even if constructors and destructors have
no side effects.

Now, to my questions.
1. Is my interpretation of the standard correct?
2. Is this behaviour intended?

Regards,
&rzej


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Daniel Krügler

unread,
Jan 16, 2012, 4:02:53 PM1/16/12
to
On 2012-01-16 00:15, Andrzej Krzemieński wrote:
> The current standard says (12.8, p. 31 and 32) that the copy/move from
> an automatic object into a temporary returned by a function as well as
> the copy/move from a temporary to an automatic object can be elided,
> thus avoiding any copying/moving in the following example.
>
> vecotr<string> generate()
> {
> vecotr<string> ans;
> populate(ans);
> return ans;
> }
>
> auto vec = generate();

In the following I assume "vecotr<string>" is supposed to be "vector<string>" (This is not really necessary, but it helps reflecting upon assumed semantics of your incomplete code).

> The standard also says that this optimization is even allowed if copy/
> move ctor or destructor have side effects. This somehow implies that
> it is only constructor's or destructor's side effects that can affect
> program's behaviour if elided.

I don't think that only a restrictive form of side-effect elimination is implied by the current wording as you describe. The current wording describes a specific *model* transformation and from this side-effects can be deduced.

> But how about the following case, if I
> slightly change function generate():
>
> vecotr<string> generate()
> {
> vecotr<string> ans;
> populate(ans);
> ScopeGuard g = [&]{ ans.clear(); }
> return ans;
> }
>
> ScopeGuard woks such that it registers a callback in the constructor,
> and calls the callback in the destructor. If copy elision is not
> performed, the additional call to function clear() has no impact on
> the program, because we are working on a local copy. However, if the
> copies are elided, the auto variable 'ans' inside the function, and a
> temporary, and the initialized global variable 'vec' are to be treated
> as the same object. In this case, if I interpret the standard
> correctly, the value of global 'vec' will be cleared. This would not
> have been the case if the moves were not elided. Thus, copy elision
> may change program behaviour even if constructors and destructors have
> no side effects.
>
> Now, to my questions.
> 1. Is my interpretation of the standard correct?

Yes. Note that p31 says:

"In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization."

This "treatment" description makes clear that during RVO there is only one object where without RVO there are two objects. Your test case just makes it observable whether the single object (RVO transformation model) or the first object (non-RVO model) is modified, which is fine.

> 2. Is this behaviour intended?

I don't know, but the wording looks pretty clear to me. In your example the appropriate way to write portable code is to rewrite your modified code to the form:

vector<string> generate()
{
vector<string> ans;
populate(ans);
{
ScopeGuard g = [&]{ ans.clear(); }
}
return ans;
}

HTH & Greetings from Bremen,

Daniel Krügler

Dave Abrahams

unread,
Jan 16, 2012, 4:06:28 PM1/16/12
to
on Sun Jan 15 2012, Andrzej Krzemieński <akrzemi1-AT-gmail.com> wrote:

> Hi,
> The current standard says (12.8, p. 31 and 32) that the copy/move from
> an automatic object into a temporary returned by a function as well as
> the copy/move from a temporary to an automatic object can be elided,
> thus avoiding any copying/moving in the following example.
>
> vecotr<string> generate()
> {
> vecotr<string> ans;
> populate(ans);
> return ans;
> }
>
> auto vec = generate();
>
> The standard also says that this optimization is even allowed if copy/
> move ctor or destructor have side effects. This somehow implies that
> it is only constructor's or destructor's side effects that can affect
> program's behaviour if elided.

FWIW, in your example, it is *still* the side effects of a destructor
that affect behavior ;-)

> But how about the following case, if I
> slightly change function generate():
>
> vecotr<string> generate()
> {
> vecotr<string> ans;
> populate(ans);
> ScopeGuard g = [&]{ ans.clear(); }
> return ans;
> }
>
> ScopeGuard woks such that it registers a callback in the constructor,
> and calls the callback in the destructor. If copy elision is not
> performed, the additional call to function clear() has no impact on
> the program, because we are working on a local copy. However, if the
> copies are elided, the auto variable 'ans' inside the function, and a
> temporary, and the initialized global variable 'vec' are to be treated
> as the same object. In this case, if I interpret the standard
> correctly, the value of global 'vec' will be cleared. This would not
> have been the case if the moves were not elided. Thus, copy elision
> may change program behaviour even if constructors and destructors have
> no side effects.
>
> Now, to my questions.
> 1. Is my interpretation of the standard correct?

It appears to be consistent with existing implementations, anyway. I
tested using https://gist.github.com/1618785

> 2. Is this behaviour intended?

I don't know about /intended/, exactly. I would call it a
to-be-expected side-effect of allowing copy elision.

Destructors with side-effects can obviously perturb value semantics (or
anything else), and copy elision essentially assumes value semantics.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
0 new messages