How safe is returning a value by reference?

D

davidkevin

Hi,
I have seen an interesting example of applications of returning a value by reference from method. But is it really safe? If not could you give me someclear examples which will show when it is unsafe? Stroustrup has given in TC++PL among others following sample:

class Matrix {
friend Matrix& operator+(const Matrix&, const Matrix&)
};

He have claimed that above code is proper but it causes problems with memory allocation. I am not sure what he means.

He has stated that:
1. a reference to a result will be passed outside the function as a reference to a value passed from a function, so that value can't be an automatic variable.
2. Operator is often used in an expression more than once so it can't be a local static variable.
3. Copying a passed value is usually cheaper (with respect to time of execution, size of code and size of data).

I would like to know what are reasons for which above sentences holds. Again, I would be grateful for clear examples.

Next - Stroustrup claim that passing by reference could cause an improvement of efficiency (however his third from above statements shows that not necessarily). Ok, but I wonder what happens in code like that:

Matrix a, b;
Matrix c;
c=a+b;

If operator+ returns a value by value then that value is returned by returnthis ; inside operator+ definition. Then a compiler can directly assign c to newly created object.

If operator+ returns a value by a reference then a copy has to be created -in other case c would be a same object which a+b is. But syntax of the assignment denies it - a reference is nothing other than a synonym for an object so above instruction has to mean that a value is assigned (and not object).

So as far as I understand when an assignment or an initialization is executed returning a value by a reference has no impact for capacity. Am I right?

Later, Stroustrup has given an example of the technique about which he has said that it causes that a result is not copied:

const int max_matrix_tem = 7;

Matrix& give_matrix_tem() {
static int nbuf = 0;
static Matrix buf[max_matrix_tem] nbuf = 0;
return buf[nbuf++];
}

Matrix& operator+(const Matrix& arg1, const Matrix& arg2) {
Matrix& res = give_matrix_tem();
//...
return tem;
}

As far as I understand above code should help to improve efficiency if we have
an expression in which there is more than one operator+. But when an assignment to give_matrix_tem should be executed? What should be assigned to give_matrix_tem? It looks that in the situation in which buf is full filled a value which give_matrix_tem() will return next will be old one. And how above code make that a copying of value is avoided?

Thanks in advance for responses,
Greetings.
 
V

Victor Bazarov

Hi, I have seen an interesting example of applications of returning a
value by reference from method. But is it really safe? If not could
you give me some clear examples which will show when it is unsafe?
Stroustrup has given in TC++PL among others following sample:

class Matrix { friend Matrix& operator+(const Matrix&, const
Matrix&) };

He have claimed that above code is proper but it causes problems with
memory allocation. I am not sure what he means.

He has stated that: 1. a reference to a result will be passed outside
the function as a reference to a value passed from a function, so
that value can't be an automatic variable. 2. Operator is often used
in an expression more than once so it can't be a local static
variable. 3. Copying a passed value is usually cheaper (with respect
to time of execution, size of code and size of data).

I would like to know what are reasons for which above sentences
holds. Again, I would be grateful for clear examples.

The (1) seems convoluted too much (did you re-type it from any of his
books, by memory?). (1) You cannot return a reference to an automatic
object because by the time you get to use that reference, the object has
been already destroyed. (2) You cannot return a reference to a static
object inside the op+ function because the function could be used more
than once in the same expression, and hence needs to be safely re-entrant.
Next - Stroustrup claim that passing by reference could cause an
improvement of efficiency (however his third from above statements
shows that not necessarily). Ok, but I wonder what happens in code
like that:

Matrix a, b; Matrix c; c=a+b;

If operator+ returns a value by value then that value is returned by
return this ; inside operator+ definition. Then a compiler can
directly assign c to newly created object.

If operator+ has a 'this' to return, it's a non-static member function,
yes? To be that it has to have only one explicit argument, the
right-hand side of the + expression, yes? Then what does it change, the
left-hand side? IOW, in the expression a+b, the matrix 'a' will be
changed? That's absurd.

So, conclusion then is that operator+ cannot 'return this'. It can only
be a friend function or a const member function that *still* returns a
new object, but not 'this'.
If operator+ returns a value by a reference then a copy has to be
created - in other case c would be a same object which a+b is. But
syntax of the assignment denies it - a reference is nothing other
than a synonym for an object so above instruction has to mean that a
value is assigned (and not object).

Can you rephrase that? I have hard time following your thought.

There are certain things you might want to read up on. First, RVO
(return value optimization). The expression c=a+b causes two (usually)
function calls, op+ and op=. If op+ returns a temporary, then the
compiler could optimize away the call to op= to actually use the 'c'
object as the place where the temporary is created for the result of the
addition. So, no copying is involved. Second, perhaps you should get
familiar with C++0x feature called "move assignment", which could help
in this case, giving another way for the compiler to optimize away
copying if it's not necessary since the object *from* which the contents
are copied during the assignment, is temporary and doesn't have to survive.
So as far as I understand when an assignment or an initialization is
executed returning a value by a reference has no impact for capacity.
Am I right?

What's "impact for capacity"? Please define that before I can answer
your "am I right" question.
Later, Stroustrup has given an example of the technique about which
he has said that it causes that a result is not copied:

const int max_matrix_tem = 7;

Matrix& give_matrix_tem() { static int nbuf = 0; static Matrix
buf[max_matrix_tem] nbuf = 0; return buf[nbuf++]; }

Something is not right in that code. A semicolon seems missing somewhere.
Matrix& operator+(const Matrix& arg1, const Matrix& arg2) { Matrix&
res = give_matrix_tem(); //... return tem; }

As far as I understand above code should help to improve efficiency
if we have an expression in which there is more than one operator+.

Yes, a temporary storage is used for the result of op+. That storage is
allocated once and reused, in hopes that none of functions that make use
of that storage are called in some expressions more than 7 times.
But when an assignment to give_matrix_tem should be executed?

Some time inside the op+ after you got the 'res' reference.
What
should be assigned to give_matrix_tem?

Nothing. You should think of terms of 'res' when implementing the op+
function that uses 'give_matrix_tem'.
It looks that in the situation
in which buf is full filled a value which give_matrix_tem() will
return next will be old one. And how above code make that a copying
of value is avoided?

Returning a reference to an existing object does not involve copying.
Since 'res' in op+ is a reference to an existing storage object (one of
the elements of the 'buf' array), no copying happens. Or maybe I don't
understand what you're asking...

V
 
D

davidkevin

W dniu sobota, 28 lipca 2012 23:15:15 UTC+2 użytkownik Victor Bazarov napisał:
The (1) seems convoluted too much (did you re-type it from any of his

books, by memory?).

The above text is taken from "The C++ Programming Language", Seventh Edition, chapter 11.6 (Operator overloading: Large objects). I had that book before my eyes when I was writing my post. But it it is not rewritten a character to a character - I have a polish edition.
(1) You cannot return a reference to an automatic
object because by the time you get to use that reference, the object has
been already destroyed.

Understand I things proper that it means that in the situation in which I have
a variable which was declared in method m() I can not return a reference tothat object? (It seems to me that more complex situations could lead to such error but that example has only show about what the problem is).
(2) You cannot return a reference to a static
object inside the op+ function because the function could be used more
than once in the same expression, and hence needs to be safely re-entrant..

OK. But how returning the reference to the static object can make that re-entrant will not be safe?
If operator+ has a 'this' to return, it's a non-static member function,
yes? To be that it has to have only one explicit argument, the
right-hand side of the + expression, yes? Then what does it change, the
left-hand side? IOW, in the expression a+b, the matrix 'a' will be
changed? That's absurd.
So, conclusion then is that operator+ cannot 'return this'. It can only
be a friend function or a const member function that *still* returns a
new object, but not 'this'.

Yes, I have made a terrible mistake. I did mean a situation in which a new
object is returned (which is a sum of a and b).
Can you rephrase that? I have hard time following your thought.

I want to know if returning a reference make it possible to improve an efficiency of calculations in instructions like:

c=a+b;

In other words: if we have instructions:

c=a+b; //operator+ returns by a value
f=d+e; //operator+ returns by a reference

will one of those more efficient than other? For me it seems that not. In
fist case return instruction inside operator+ will make that one copy of object will be created. When the assignment will be executed a next copy has not be created because a+b is temporary so we can use it directly (and I think that every good compiler will not make that unnecessary copy). In second case a value is returned by a reference so object is not copied here. Butcopy has to be executed in order to make the assignment possible.
So in both cases one copy is created. So there is no reason for which one version should be more efficient than other. Am I right?
What's "impact for capacity"? Please define that before I can answer
your "am I right" question.

Have I used a word "capacity" incorrectly? What I mean was that returning areference has no impact for efficiency.
Later, Stroustrup has given an example of the technique about which
he has said that it causes that a result is not copied:

const int max_matrix_tem = 7;

Matrix& give_matrix_tem() { static int nbuf = 0; static Matrix
buf[max_matrix_tem] nbuf = 0; return buf[nbuf++]; }



Something is not right in that code. A semicolon seems missing somewhere..

const int max_matrix_tem = 7;

Matrix& give_matrix_tem() {
static int nbuf = 0;
static Matrix buf[max_matrix_tem] nbuf = 0;
if (nbuf==max_matrix_tem) nbuf = 0; //missed line
return buf[nbuf++];
}
Yes, a temporary storage is used for the result of op+. That storage is

allocated once and reused, in hopes that none of functions that make use

of that storage are called in some expressions more than 7 times.



Some time inside the op+ after you got the 'res' reference.
Nothing. You should think of terms of 'res' when implementing the op+
function that uses 'give_matrix_tem'.
Returning a reference to an existing object does not involve copying.
Since 'res' in op+ is a reference to an existing storage object (one of
the elements of the 'buf' array), no copying happens. Or maybe I don't
understand what you're asking...

That conception (using a buffer of static objects) is still not very clear for me. What is a reason for which operator+ know that give_matrix_tem willgive him an object which it that operator really needs?
 
J

James Kanze

On 7/28/2012 2:23 PM, (e-mail address removed) wrote:

I think you (the original poster) have slightly misunderstood.
The above code is "correct", in that it doesn't violate any of
the syntactic rules of C++. It's not correct because of
lifetime of object issues; the reference you return refers to an
object which ceases to exist when you return.

I'm sure he expressed it more clearly than that. The returned
reference refers to a local variable, which ceases to exist once
you've left the function. (This is called a dangling
reference.)
The (1) seems convoluted too much (did you re-type it from any of his
books, by memory?). (1) You cannot return a reference to an automatic
object because by the time you get to use that reference, the object has
been already destroyed. (2) You cannot return a reference to a static
object inside the op+ function because the function could be used more
than once in the same expression, and hence needs to be safely re-entrant.

Reentrant isn't really the word which corresponds, since
reentrant refers to being in the same function several times at
once. But I can't find a good word to specify the quality
needed.
If operator+ has a 'this' to return, it's a non-static member function,
yes? To be that it has to have only one explicit argument, the
right-hand side of the + expression, yes? Then what does it change, the
left-hand side? IOW, in the expression a+b, the matrix 'a' will be
changed? That's absurd.
So, conclusion then is that operator+ cannot 'return this'. It can only
be a friend function or a const member function that *still* returns a
new object, but not 'this'.
Can you rephrase that? I have hard time following your thought.
There are certain things you might want to read up on. First, RVO
(return value optimization). The expression c=a+b causes two (usually)
function calls, op+ and op=. If op+ returns a temporary, then the
compiler could optimize away the call to op= to actually use the 'c'
object as the place where the temporary is created for the result of the
addition.

Not in this case (assignment). The c object has already been
constructed, so the compiler cannot use it for another object
which will be newly constructed.
So, no copying is involved.

No copy construction is involved. The compiler cannot optimize
away copy assignment (or at least, not easily).
Second, perhaps you should get
familiar with C++0x feature called "move assignment", which could help
in this case, giving another way for the compiler to optimize away
copying if it's not necessary since the object *from* which the contents
are copied during the assignment, is temporary and doesn't have to survive.

Introducing move semantics at the point where the original
poster is may be a bit premature. They're an advanced
technique, which can be ignored by most programmers.
What's "impact for capacity"? Please define that before I can answer
your "am I right" question.

He seems to be inventing a special vocabulary of his own.
Replace "capacity" by "storage" or "storage duration", and it
makes a little more sense.
Later, Stroustrup has given an example of the technique about which
he has said that it causes that a result is not copied:
const int max_matrix_tem = 7;

Matrix& give_matrix_tem()
{
static int nbuf = 0;
static Matrix buf[max_matrix_tem] nbuf = 0;
return buf[nbuf++];
}
Something is not right in that code. A semicolon seems missing somewhere.

I'd guess that the second "nbuf = 0" is noise, and shouldn't be
there. Without it, this was a classic technique to avoid deep
copy of return values. It's also a very dangerous techique: it
works for up to 7 (in this case) return values, and then fails
in a difficult to detect manner. (Difficult to detect, because
the failure only occurs in complex expressions, where one of the
return values sometimes gets overwritten.)

This is a technique which should only be used with extreme
caution, in very restricted contexts, by programmers who really
know what they're doing. If Stroustrup actually recommends this
in his recent book (the one for beginners), then I'm a bit
disappointed.

(The technique can result in a significant improvement in
performance. But there are other techniques which can be used
to avoid the copy as well.)
Yes, a temporary storage is used for the result of op+. That storage is
allocated once and reused, in hopes that none of functions that make use
of that storage are called in some expressions more than 7 times.

And in the hopes that the code will never be used in a
multithreaded environment.
Some time inside the op+ after you got the 'res' reference.
 
S

SG

Am Sonntag, 29. Juli 2012 10:30:30 UTC+2 schrieb (unbekannt):
[...]
The above text is taken from "The C++ Programming Language", Seventh
Edition, chapter 11.6 (Operator overloading: Large objects). I had
that book before my eyes when I was writing my post. But it it is
not rewritten a character to a character - I have a polish edition.
(1) You cannot return a reference to an automatic
object because by the time you get to use that reference,
the object has been already destroyed.
Right.

Understand I things proper that it means that in the situation in
which I have a variable which was declared in method m() I can not
return a reference to that object?

If it's an _automatic_ object (an object stored in the automatic memory in the scope of the function) it will cease to exist once the execution returns to the caller. Returnung a reference to this object is pointless since you cannot access it anymore through that reference outside the function because the object does not exist anymore.

Well, you "can" do lots of things. But you should not because most of it does not make sense and/or is a bad idea.
OK. But how returning the reference to the static object can make
that re-entrant will not be safe?

Sorry, what?
[...]
I want to know if returning a reference make it possible to improve
an efficiency of calculations in instructions like:

c=a+b;

In other words: if we have instructions:

c=a+b; //operator+ returns by a value

f=d+e; //operator+ returns by a reference

will one of those more efficient than other? For me it seems that
not. In fist case return instruction inside operator+ will make
that one copy of object will be created.

What copy do you mean?
When the assignment will
be executed a next copy has not be created because a+b is
temporary so we can use it directly (and I think that every good
compiler will not make that unnecessary copy).

This does not make sense to me. First, you say a copy has to be created then you say that a decent compiler will optimize it away. There is nothing a compiler can do about assignments. If you have an assignment, the appropriate operator= function gets excecuted. That's it.
In second case a
value is returned by a reference so object is not copied here.
But copy has to be executed in order to make the assignment possible.

So in both cases one copy is created. So there is no reason for which
one version should be more efficient than other. Am I right?

In my opinion, efficiency matters little if the "faster" version is not re-entrant or cannot be used in a multi-threaded environment or even returns dangling references.
const int max_matrix_tem = 7;

Matrix& give_matrix_tem() {
static int nbuf = 0;
static Matrix buf[max_matrix_tem] nbuf = 0;
if (nbuf==max_matrix_tem) nbuf = 0; //missed line
return buf[nbuf++];
}

Please don't ever write horrible code like this.

If you're concerned about efficiency, please look elsewhere. For example:
- move semantics
- copy on write.
- expression templates (lazy evaluation)

Cheers!
 
J

James Kanze

W dniu sobota, 28 lipca 2012 23:15:15 UTC+2 użytkownik Victor Bazarov napisał:
On 7/28/2012 2:23 PM, (e-mail address removed) wrote:
[...]
The above text is taken from "The C++ Programming Language",
Seventh Edition, chapter 11.6 (Operator overloading: Large
objects). I had that book before my eyes when I was writing my
post. But it it is not rewritten a character to a character -
I have a polish edition.

Don't. Regretfully, translations of technical books are almost
always miserable, and frequently misleading. Stroustrup is one
of the rare authors who understands the problem, and does what
he can to fix it, when he is made aware of it. But globally,
you're better off struggling in English with the original. You
won't have to deal with misleading translations, and when you do
post a question in an English speaking forum, you'll be able to
use the correct vocabulary, instead of trying to guess the
original word that the translator translated with whatever in
your language. And of course, the more you read in English,
the less you'll have to struggle.

(I really regret having to give this advice, because I think you
should be able to use your own language. But the reality is
that you can't.)
Understand I things proper that it means that in the situation
in which I have a variable which was declared in method m() I
can not return a reference to that object? (It seems to me
that more complex situations could lead to such error but that
example has only show about what the problem is).

I think you need some clarification with regards to the language
you're using. All *objects* have a specific lifetime. (A
variable is a name bound to an object.) If you define a
non-static variable with local scope (within a block of code),
the object has something known as "automatic" lifetime, which
means it ceases to exist when the name goes out of scope.
(Unless, of course, the variable has reference type, in which
case, with one major exception which doesn't concern us here, it
has no effect on the object lifetime.)

A reference is not an object, but creates another way of
referring to an existing object. If the lifetime of the
reference exceeds that of the object, you have a dangling
reference. Which is undefined behavior---another way of saying
that anything might happen.
OK. But how returning the reference to the static object can
make that re-entrant will not be safe?

In more complicated expressions, like "(a+b) * (c+d)", the
return value of "a+b" and of "c+d" refer to the same object. If
the two return values are supposed to have different values, one
of them will have been overwritten when you get to the *
operator.
I want to know if returning a reference make it possible to
improve an efficiency of calculations in instructions like:

It might, or it might not. In the general case, if copying a
Matrix is expensive, it probably will improve efficiency. At
the cost of giving incorrect results in more complicated
expressions.
In other words: if we have instructions:
c=a+b; //operator+ returns by a value
f=d+e; //operator+ returns by a reference
will one of those more efficient than other? For me it seems
that not. In fist case return instruction inside operator+
will make that one copy of object will be created. When the
assignment will be executed a next copy has not be created
because a+b is temporary so we can use it directly (and I
think that every good compiler will not make that unnecessary
copy). In second case a value is returned by a reference so
object is not copied here. But copy has to be executed in
order to make the assignment possible.

The assignment will still copy. On the other hand, the return
value will not be copied from any local variable.

Most compilers today implement some form the return value
optimization, which will avoid the copy in well written code.
(In at least one compiler, "well written code" means no more
than a single return statement in the function.)
So in both cases one copy is created. So there is no reason
for which one version should be more efficient than other. Am
I right?

Without the return value optimization, return by value would
result in a copy in the function; the return value will be
copied from a local variable or a temporary into where ever the
compiler puts class type return values. (The memory used for
local variables typically disappears when you return from the
function.) The return value optimization means that this copy
is suppressed.

[...]
[...]
const int max_matrix_tem = 7;
Matrix& give_matrix_tem() {
static int nbuf = 0;
static Matrix buf[max_matrix_tem] nbuf = 0;

There's still too much on the preceding line.
if (nbuf==max_matrix_tem) nbuf = 0; //missed line
return buf[nbuf++];
}
That conception (using a buffer of static objects) is still
not very clear for me. What is a reason for which operator+
know that give_matrix_tem will give him an object which it
that operator really needs?

Don't worry about it. It's an advanced technique, to be learned
only after you've mastered the basic lifetime issues. The basic
rule is:

-- return a reference when you want to allow access to data
held by your object (e.g. something like the operator[] on a
vector).

-- return a value in all other cases.

-- pass class types by reference to const, all other types by
value.

-- and don't use references anywhere but in the above cases.

That's sufficient for all but the most advanced programmers. (I
can't remember the last time I did anything else, and I've 30
years experience in C++, much of it in very low level,
performance oriented code.)
 
D

davidkevin

OK, thanks to all for the discussion, every post was valuable for me but last post by James Kanze make things completely clear and I have understood already how the technique of buffering static objects could help in the improvement of calculations efficiency.

BTW: James Kanze, I believe that a book which you described as a "book for beginners" is "Programming: Principles and Practice using C++" but it isn'tone which I use now. The book which I read is "The C++ Programming Language". It is the fact that Stroustrup has shown a sample of the technique mentioned above but he explicitly stated that if somebody will write an expression which would require more temporary variables than max_matrix_tem then he is asking about enter into real troubles.

Greetings.
 
J

James Kanze

BTW: James Kanze, I believe that a book which you described as a "book for
beginners" is "Programming: Principles and Practice using C++"

That's the one I was referring to. If you've no great
experience programming, it's by far the best book I've seen for
starting.
but it isn't
one which I use now. The book which I read is "The C++ Programming Language".

That's an excellent reference, but it's more oriented to
advanced programmers (IMHO, anyway).
It is the fact that Stroustrup has shown a sample of the technique mentioned
above but he explicitly stated that if somebody will write an expression
which would require more temporary variables than max_matrix_tem then he is
asking about enter into real troubles.

Yes. Stroustrup tends to understand these sort of things. It's
a useful technique in certain restricted contexts. It was also
much abused in earlier days (before RVO and NRVO were well
known).
 

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,955
Messages
2,570,117
Members
46,705
Latest member
v_darius

Latest Threads

Top