must pass by value

P

pauldepstein

My texts give plenty of examples where passing by reference is
necessary. For instance, the famous swap example.

However, I've never seen an example where passing a variable by value
is necessary.

One place where I've seen passing by value recommended is in the
throwing of exceptions. However, my texts never say why throwing by
reference is wrong. (Of course, lots of people have explained why
catching by value is poor practice.) Suppose I did throw by reference
using namespace std; and including the correct exceptions libraries.
Could I then write:

throw &runtime_error("Incorrect...blah blah ");

Or would this code be incorrect? My thinking is that a runtime_error
is initialized with the string "Incorrect...blah blah " and that a
reference to that error is thrown.

Can anyone give a link to or give an example where the technique of
passing by value is necessary to make a function work, and where
passing by reference would lead to problems.

I see two reasons to sometimes pass by value (not sure if this is
correct). 1) Passing by value is quicker for simple built-in types
like int. 2) Passing by value is easier to code (though the greater
ease probably only makes a difference to beginners.)

This doesn't seem enough of a reason for the language to include the
passing-by-value concept.

Are there reasons not to pass everything by value?


Thank you for your explanations.

Paul Epstein
 
R

reckless2k

I suppose one disadvantage would be that you could not concurrently
modify the input when it is a reference... this is no problem most
times, but I suppose there are situations where you might not want to
have to lock the passed value until the caller finishes with it.

just a thought
 
B

Bo Persson

My texts give plenty of examples where passing by reference is
necessary. For instance, the famous swap example.

However, I've never seen an example where passing a variable by
value
is necessary.

That comes from the C language, where pass by value was the norm, and
references isn't available.
One place where I've seen passing by value recommended is in the
throwing of exceptions. However, my texts never say why throwing by
reference is wrong. (Of course, lots of people have explained why
catching by value is poor practice.) Suppose I did throw by
reference
using namespace std; and including the correct exceptions libraries.
Could I then write:

throw &runtime_error("Incorrect...blah blah ");

Or would this code be incorrect? My thinking is that a
runtime_error
is initialized with the string "Incorrect...blah blah " and that a
reference to that error is thrown.

Don't know really. What would be the advantage? The throwing logic
will often have to make a copy anyway, to ensure that the exception is
available at a far away catch point.
Can anyone give a link to or give an example where the technique of
passing by value is necessary to make a function work, and where
passing by reference would lead to problems.

One advantage of call by value is that the function gets its own copy
of the parameter. That ensures that nobody else is fiddling with it at
the same time. It can also be used as a local work space inside the
function.
I see two reasons to sometimes pass by value (not sure if this is
correct). 1) Passing by value is quicker for simple built-in types
like int.

Not by much. Having your own copy may make the functions machine code
simpler though.
2) Passing by value is easier to code (though the greater
ease probably only makes a difference to beginners.)

Passing by value and passing by const reference looks pretty much the
same, both att the call site and inside the fucntion.
This doesn't seem enough of a reason for the language to include the
passing-by-value concept.

For C++ it is inherited from C, where it was the only one.
Are there reasons not to pass everything by value?

Sometimes you want to update the parameter, which can be done with a
non-const reference.


Bo Persson
 
P

pauldepstein

I suppose one disadvantage would be that you could not concurrently
modify the input when it is a reference... this is no problem most
times, but I suppose there are situations where you might not want to
have to lock the passed value until the caller finishes with it.

just a thought

Either I misunderstand you or you are wrong. (I'm happy to assume my
poor understanding is at fault.)

I coded and ran the following program successfully:

#include <iostream>
using namespace std;

int main()
{
int y = 1;
int f(int&);
cout << f(y);
cin.get();

}

int f(int &x)
{ x = x +2;
return x;}

I therefore appear to have modified the input. (Though I don't know
whether I "concurrently" modified it.)

x = x+2 modifies input with the reference call f(int &x) so it appears
(to me) that I have contradicted your posting.

Thanks,

Paul
 
J

Jens Theisen

However, I've never seen an example where passing a variable by value
is necessary.

To some extend, it's a convenience for a copy, and you're not
disputing the necessity for making copies, are you?

Also, there is a major argument dealing with lifetime.

References generally don't extend lifetime of the object they refer
to. If the language provided no value semantics, how would you
transfer ownership to a function? The calling code would need to
ensure that the reference is valid until it's not needed anymore.

In fact, this is pretty much what C has: You can pass by value only in
a "built-in" sense, ie. you can pass objects only by bit-wise
copies. So, if you want to transfer ownership, the language will not
help you.

C++ on the other hand, has smart pointer which are, when passed by
value, transfer ownership (or other things, depending on the smart
pointer).
throw &runtime_error("Incorrect...blah blah ");

This is not throwing by reference but by pointer, and does so in a
wrong way. The catch handler will get a pointer to the memory where
the temporary runtime_error has once been. Using that pointer is
undefined behaviour.

Better is

throw new runtime_error("...");

However, you now have to delete the exception in all your catch
handlers that could catch it. That's error prone and thus

throw runtime_error("...");

is preferred.
Are there reasons not to pass everything by value?

I think you mean by reference. As a particular compelling example, try
do define the following function sensibly and run your solution:

int const& product(int const& lhs, int const& rhs)
{
...
}

Regards,

Jens
 
A

Andrew Koenig

This doesn't seem enough of a reason for the language to include the
passing-by-value concept.

Well, strictly speaking, it's not necessary -- you can accomplish the same
thing by passing a reference and copying it yourself.

However, there are times when it is useful. For example:

void reduce(vector<double>& v, const double& x)
{
for (vector<double>::iterator it = v.begin(); it != v.end(); ++it)
*it /= x;
}

This function divides every element of a vector by a given value. Now
suppose we call it this way:

vector<double> v;
v.push_back(10);
v.push_back(20);
reduce(v, v[0]);

You might think that the call

reduce(v, v[0]);

would have the same effect as

reduce(v, 10);

because the value of v[0] is 10. You would be wrong.

What happens is that v[0] gets set to v[0]/v[0], which is, 1, and then v[1]
gets set to v[1]/v[0]. Because v[0] is now 1, the effect is to leave the
value of v[1] unchanged.

If "const double& x" is changed to "double x", the problem goes away.
 
O

Old Wolf

Jens said:
This is not throwing by reference but by pointer, and does so in a
wrong way. The catch handler will get a pointer to the memory where
the temporary runtime_error has once been. Using that pointer is
undefined behaviour.

Actually this code is ill-formed; you can't take the address of a
temporary.
 
J

Jens Theisen

Old Wolf said:
Actually this code is ill-formed; you can't take the address of a
temporary.

I don't think that is true. Temporaries are objects as any other, and
putting an ampersand before them has the same semantics as for any
other object.

Jens
 
F

Frederick Gotham

Jens Theisen posted:
I don't think that is true. Temporaries are objects as any other, and
putting an ampersand before them has the same semantics as for any
other object.


A "nameless object", as they're called, is an R-value, not an L-value. One
cannot take the address of an R-value; the compiler must issue a diagnostic
if you attempt to do so.

The following won't compile:

int main()
{
int() = 5; /* Can't assign to an R-value */
int *p = &int(); /* Can't take address of R-value */
}

The reason why it works with user-defined types is that the two lines are
interpreted respectively as:

MyClass().operator=(5);
MyClass *p = MyClass().operator&();

Whether you think this is appropriate or not is a topic for comp.std.c++.
 
K

Kai-Uwe Bux

Jens said:
I don't think that is true. Temporaries are objects as any other, and
putting an ampersand before them has the same semantics as for any
other object.

From the standard [5.3.1/2]

The result of the unary & operator is a pointer to its operand. The
operand shall be an lvalue or a qualified-id.


Note that in

&runtime_error("Incorrect...blah blah ")

the operator & is applied to neither an lvalue nor a qualified-id.


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

Frederick Gotham wrote:

[snip]
The following won't compile:

int main()
{
int() = 5; /* Can't assign to an R-value */
int *p = &int(); /* Can't take address of R-value */
}

The reason why it works with user-defined types is that the two lines are
interpreted respectively as:

MyClass().operator=(5);
MyClass *p = MyClass().operator&();

Ah, I missed that.


Thanks

Kai-Uwe Bux
 
A

Alf P. Steinbach

* Kai-Uwe Bux:
Frederick Gotham wrote:

[snip]
The following won't compile:

int main()
{
int() = 5; /* Can't assign to an R-value */
int *p = &int(); /* Can't take address of R-value */
}

The reason why it works with user-defined types is that the two lines are
interpreted respectively as:

MyClass().operator=(5);
MyClass *p = MyClass().operator&();

Ah, I missed that.

I don't see the original posting, but I don't think you missed anyhing:
the compiler does not generate an operator& automatically for a
user-defined data type. It's the plain old built-in one that comes into
play unless the class defines an operator&, and the built-in one can't
be applied to a temporary -- although a compiler may allow that as a
language extension, similar to passing a T temporary to a T& argument
(also, as I recall there was an error in early editions of "Thinking in
C++" where operator& was listed among the auto-generated ones).
 
K

Kai-Uwe Bux

Alf said:
* Kai-Uwe Bux:
Frederick Gotham wrote:

[snip]
The following won't compile:

int main()
{
int() = 5; /* Can't assign to an R-value */
int *p = &int(); /* Can't take address of R-value */
}

The reason why it works with user-defined types is that the two lines
are interpreted respectively as:

MyClass().operator=(5);
MyClass *p = MyClass().operator&();

Ah, I missed that.

I don't see the original posting,

My posting is a sibling to Frederick Gotham's posting, which I quoted.
but I don't think you missed anyhing:
the compiler does not generate an operator& automatically for a
user-defined data type. It's the plain old built-in one that comes into
play unless the class defines an operator&, and the built-in one can't
be applied to a temporary -- although a compiler may allow that as a
language extension, similar to passing a T temporary to a T& argument
(also, as I recall there was an error in early editions of "Thinking in
C++" where operator& was listed among the auto-generated ones).

Clearly, I am getting sleepy: g++ confused me by not producing an error but
just a warning. Comeau does give an error on:

#include <stdexcept>

int main ( void ) {
& std::runtime_error( "hello" );
}

That is in line with my original expectations.


Thanks

Kai-Uwe Bux
 
J

Jens Theisen

Kai-Uwe Bux said:
From the standard [5.3.1/2]

The result of the unary & operator is a pointer to its operand. The
operand shall be an lvalue or a qualified-id.

Sorry, I should have checked.

Jens
 

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,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top