From 9047358884776869130
X-Google-Language: ENGLISH,ASCII-7-bit
X-Google-Thread: f78e5,f5d433881cc29cff
X-Google-Attributes: gidf78e5,public
From: jpotter@falcon.lhup.edu (John Potter)
Subject: Named return value optimisation- reprise.
Date: 1997/06/17
Message-ID: <199706172326.SAA05629@csrlink.net>
X-Deja-AN: 249172268
X-Original-Date: Tue, 17 Jun 1997 22:33:00 GMT
Organization: -
X-Auth: PGPMoose V1.1 PGP comp.std.c++ iQBVAwUBM6cTAky4NqrwXLNJAQF+3QIAu5PPONJihl4l75syDaGC9ozfICTbmnvG kK+pYx386x04qv9nhP6aFo7Ocf0t4D98sAwsxa3jfrAFt4C5h/k0kg== =ytob
Newsgroups: comp.std.c++
Originator: austern@isolde.mti.sgi.com


On 17 Jun 97 09:10:06 GMT, bparker@gil.com.au (Brian Parker) wrote:

: On 16 Jun 97 17:46:06 GMT, jpotter@falcon.lhup.edu (John Potter)
: wrote:

: Your argument is essentially that in the same way that
: Vec v3 = Vec(c_array);   	// (1)
: can be transformed by the compiler to 
: Vec v3(c_array);   	// (2)
: then the return value of InitVector can be optimised away.

Yes

: I would agree with this argument, except that I don't think that even
: this optimisation is allowed by 12.2, and so it too relies upon
: 12.8/15.

I don't think so.  12.2/1 lists places where the implementation may
need to create temporaries.  It requires that the form using the
temporary must be well formed under the semantics restrictions of the
language, such as an accessable copy constructor.  Given that, the
implementation may avoid creating the temporary.  Following the links
to the places where creation of the temporary is described, there is
always a link back to 12.2 stating that the temporary may be avoided.
As I see it, 12.2 covers removal of unnamed temporaries while 12.8/15
extends this to named copies.  Both clauses are authorized violations
of the "as if" rule because they can be detected via side effects in
the ctors/dtor.

: So, if one interprets "object" in 12.8/15 to mean memory used both
: directly and indirectly, a compiler *cannot* convert (1) to (2) in
: cases such as my example- in any case, it is relying upon 12.8/15.

I do not even want to think about the ramifications of that
interpretation being inconsistently used in a few places in chapter 12
much less consistently throughout the draft.

: >  Restricting the use of 12.8 for
: >return value optimization would provide a way to code functions which
: >allow broken classes to operate, at the expense of preventing
: >temporary removal in functions using correct classes.  Use of the
: >broken classes can be coded as construction followed by assignment
: >without changing the draft.

: (actually, the way 12.8/15 is written it can elide *any* copy
: including those done via a copy assignment operator, but that is just
: an editorial problem)

Hum.  Seems I was corrected on that previously, possibly in some email
not publicly.  That may well be more than an editorial problem.

: Let me make it clear that I don't want 12.8/15 restricted *just* to
: prevent the return value optimisation. In fact my third preferred
: option would be to restrict 12.8/15 to apply *only* to initialisations
: and, therefore, the RVO. As I have previously argued, I think that
: allowing 12.8/5 to elide copies on pass-by-value is the main flaw and
: I think most people have underestimated the danger in destroying the
: fundamental axiom that pass-by-value can't alter its argument. The
: NRVO and RVO only occur in a much more circumscribed situation- i.e.
: initialisations- and so the problems can be worked around provided one
: is aware of them (by, as you suggest, using default construction
: followed by assignment rather than direct initialisation).

Vec v;
v = InitVec(c_array);
Optimize to
Vec v(InitVec(c_array));

Does 12.8/15 allow that?  Does it generally allow the implementation
to assume that construction followed by assignment can be reduced to
copy construction?

: (Incidentally, I would be interested in your opinion of my STL
: pass-by-value example in my response to Jason Merrill in this thread.
: Do you think 12.8/15 should be allowed to be applied in that case?).

Extracting the important parts
vector<int> reverse_vec(vector<int> v) {
	reverse(v.begin(), v.end());
	return v;
	}
vector<int> v1; // assign values and record begin() and end()
vector<int> v2 = reverse_vec(v1);  // v1 not used after this

Translation of last line
vector<int> v2((vector<int> v(v1), reverse(v.begin(), v.end()), v));
Optimize out v since v1 is not used again
vector<int> v2((reverse(v1.begin(), v1.end()), v1));
Optimize out v2 and replace all further uses with v1
reverse(v1.begin(), v1.end());

Seems like it is allowed.  Scary?  Do I think that it should be
allowed?  No strong feelings; so, I am willing to allow it to get
(N)RVO.  Does it scare me?  Not any more than any use of iterators.
Iterators are abstractions of pointers and just as dangerous.  In STL,
they export the internals of the container class which is always
dangerous.  I get scared any time that I declare a variable of type
iterator.  I try to only pass begin()/end() to functions.  Sometimes I
use an iterator in a for statement, and become very careful of its use
in the body.  Any more global use is cause for redesign consideration
or extreme care.

And, of course, your example is fixed by using my cautions
	transform(v1.begin(), v1.end(), v2.begin(),
			ostream_iterator<int>(cout,","), plus<int>());		
The real question is whether the example was written by Machiavelli or
Murphy?

: Having said that, I still think that the NRVO and RVO do alter the
: semantics of return-by-value and I don't agree with your assessment
: that the  MV_Vector_double class or my example class is broken. If one
: wants to use a vector class with C arrays then such a design is
: required.

Well, maybe not broken, but dangerous.  I think that the idea in the
MV class is to allow a vector on the stack by exporting implementation
details to the user.  This may be desirable; however, it sure makes it
easy to produce incorrect code.

Vec v(new int[5]);  // memory leak
I am using lots of Vecs of size three and would like to be able to
initialize them nicely.  How about a useful function:
Vec BuildThreeVec (int v0, int v1, int v2) {
	int temp[3] = { v0, v1, v2 };
	return temp;
	}
Vec v1(BuildThreeVec(1, 2, 3));
Vec v2(BuildThreeVec(4, 5, 6));
cout << v1[0] << ' ' << v1[1] << ' ' << v1[2] << endl;

I think that I am, and you have been, playing Machiavelli.  Maybe
Murphy is capable of this.

John
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your 
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu 
]



