When writing new C++ code, what is the point of passing any (input)
parameter by value when passing by const reference will just work as
well? (Even and especially PODs, I would not do it with a complex type
anyway.)
(Given that in 90% of the code you will never want to modify the
parameter anyway.)
br,
Martin
[EXAMPLE_CODE]
class foo {
};
void f_val_int(int);
void f_ref_int(const int&);
void f_val_foo(foo);
void f_ref_foo(const foo&);
int main(int,)
{
double d = 1.2;
f_val_int(d);
f_ref_int(d);
f_val_int(1.4);
f_ref_int(1.5);
foo x;
f_val_foo(x);
f_ref_foo(x);
f_val_foo(foo());
f_ref_foo(foo());
return 0;
};
void f_val_int(const int x) {
int y = x;
y++;
}
void f_ref_int(const int& x) {
int y = x;
y++;
}
void f_val_foo(const foo f) {
foo ff = f;
ff;
}
void f_ref_foo(const foo& f) {
foo ff = f;
ff;
}
[/EXAMPLE_CODE]
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
1. Passing an interger by value is usually faster than passing an integer by
reference.
2. Passing a paramater by value allows modification of the paramater as a
local variable I.E.
void Reverse( const char* CString, int length )
{
while ( --length )
{
std::cout << CString[length];
}
}
3. A lot of existing C code passes by value.
4. Sometimes it is clearer to pass by value rather than by reference. I
once toyed with changing all pointer paramters in a program to references
and some code got jsut downright ugly.
5. Choice
6+ Reasons I haven't thought of
<snip>
In theory, passing a small type (int, char, float, etc) by value will
be more efficient than passing by const reference. In your example
function "f_ref_int", the address of the int gets passed to the
function and then the value of "x" has to be fetched, whereas in
f_val_int, the value of "x" is passed directly.
In practice, it's hard to say if one would see much performance
difference without taking some measurements.
-Eric
Well, for the POD's, or any complex data type (I don't know what a POD
is), a copy constructor should make passing by const reference and
value irrelevant...well, short of efficiency.
{ Edits: quoted clc++m banner removed, since it's available at the end of this
article (and indeed, every clc++m article). There's no need to quote it. -mod }
It is because passing by reference/pointer will introduce the alias
problem, which hinder the compiler to perform a numbers of
optimizations.
Therefore, in a performance point of view, primitive types should pass
by value while large data structure should pass by reference/pointer.
More information on the aliasing problem:
http://www.ddj.com/cpp/184404273;jsessionid=FAZXOZJKL5K24QSNDLPCKHSCJUNN2JVN?_requestid=161084
Martin T. wrote:
> Hi all.
>
> When writing new C++ code, what is the point of passing any (input)
> parameter by value when passing by const reference will just work as
> well? (Even and especially PODs, I would not do it with a complex type
> anyway.)
> (Given that in 90% of the code you will never want to modify the
> parameter anyway.)
Whether passing by const reference or value, for the
callers perspective the value is not modified.
Wrt. to your question, typically I would think that passing
a builtin type by reference or const ref would cause
an unecessary level of indirection, whereas passing by
value does not cause this. Furthermore you gain nothing
when passing by const reference as opposed to passing
by value in terms of what is copied.
Therefore, passing by value the builtin type you would
gain at least the lack of indirection.
Regards,
Werner
Speed, for small types passing by value is just as fast as passing the
reference, and you do not have to pay a penalty when accessing it (a
reference is usually implemented like a pointer, so you have one lever
of indirection when accessing it).
Also, if you need a copy in the function (you can not operate on the
original object) you might as well create the copy when you call the
function instead of inside the function.
--
Erik Wikström
Efficiency.
> void f_val_int(const int x) {
> int y = x;
> y++;
>
> }
...results in thing whose size is sizeof(int) being copied to the
stack. Kein Problem.
>
> void f_ref_int(const int& x) {
> int y = x;
> y++;
>
> }
...effectively results in thing whose size is sizeof(int *) being
copied to the stack. That thing is a pointer that has to be derefenced
to get at x, theoretically causing a slight performance penalty.
> void f_val_foo(const foo f) {
> foo ff = f;
> ff;
>
> }
...results in a thing whose size is sizeof(foo) being copied to stack.
If sizeof(foo) is large number, this is obviously not good. An
unnecessary expenditure of stack memory and CPU time.
> void f_ref_foo(const foo& f) {
> foo ff = f;
> ff;}
>
Best code for passing read-only aggregates, IMHO.
-Le Chaud Lapin-
1. It's a C legacy.
2. You may want to change the (copied) value of your parameter inside
the function. e.g.
void f(int n)
{
while (n-- > 0)
{
// do something
When you receive a parameter by reference, you should keep in mind
that the actual value which is referenced can be changed by other code
- it may be const for you, but it is not necessarily const for
everyone else. Most often you don't care, but sometimes it might
matter - and in that case you need to have a copy.
Of course, there are performance reasons, too - when you get an const
int&, the compiler is much less likely to generate efficient register-
only code than for plain int - again, this is mostly because the value
referenced might be changed from other code you call. For inline
functions, a decent compiler will be able to optimize away the
references where it does not change the semantics of the function, but
I've yet to see one that can do that trick for non-inline functions,
and particularly for virtual functions.
References are implemented as pointers, so whenever you use a
reference parameter in your functions, two readings are performed:
value of a pointer to the passed variable and the variable itself.
As copy-constructors of built-in types are cheap, it's usually more
efficient to pass them by value. The same applies to STL iterators.
For all other types passing by const reference is preferable.
Regards,
Marcin Barczynski
>
> When you receive a parameter by reference, you should keep in mind
> that the actual value which is referenced can be changed by other code
> - it may be const for you, but it is not necessarily const for
> everyone else. Most often you don't care, but sometimes it might
> matter - and in that case you need to have a copy.
>
And there are massive implications when dealing with multiple threads of
concurrency. Both of these will be part of the new C++ Standard.
--
Note that robinton.demon.co.uk addresses are no longer valid.
In is true that const reference allows passing parameters with input
mode semantic but it usually requires deferencing in the function.
So it depends, in theory:
- pass-by-value requires additional storage but data is on the stack
which means it is likely to be in the cache and accesses are faster.
- pass-by-reference allows to pass only an alias (saving a copy) but
may be slower is some specific cases because reads use address.
Personally, I have never seen a case were this was an issue and my rule
of thumb is pass-by-value for scalar and small POD and
pass-by-const-reference for more complex data.
Does anyone know the impact of c++0x memory model on this issue: is the
data passed by const reference likely to be put in cache or is it
fetched each time to allow concurrent read/write ?
--
Michael
> When writing new C++ code, what is the point of passing any (input)
> parameter by value when passing by const reference will just work as
> well?
One possibility is that you might be mistaken about whether it works just as
well.
Here's an example:
// Divide all the elements of a vector<double> by a constant
void vec_divide(vector<double>& v, const double& x)
{
for (vector<double>::iterator it = v.begin(); it != v.end(); ++it)
*it /= x;
}
Now consider what happens when you call
vector<double> v;
v.push_back(1.23);
v.push_back(4.56);
vec_divide(v, v[0]);
If you don't see the problem, try running it.
This probably goes under the category of "guarding against
Machiavelli", but with pass by value, you know that the program
doesn't pull chicanery like this...
void foo(const T& in)
{
const_cast<T&>(in) = newVal;
}
John McG.
> It is because passing by reference/pointer will introduce the alias
> problem, which hinder the compiler to perform a numbers of
> optimizations.
Also, I do not know whether it is related, but I've observed than when
a function takes a reference to a T and that I'm passing to it a T
lvalue which is part of another object, optimizers keep the object
alive even if it is not needed.
While that is indeed needed for pointers, it isn't for references.
That certainly penalizes generated code for nothing.
Wow. That scares the heck out of me. Are there programming guidelines
that would help me avoid or spot such an error when coding?
int main(int argc, char *argv[]) {
int x=1;
int * p=&x;
f(p,x);
std::cout<<x;
return 0;
Heh. I Admit I have not seen the problem :)
Given that this kind of problem would exist for any type and that for
complex types one would in general pass by reference, ... under what
headline would this kind of problem be discussed? I'm sure there are
some more interesting examples (std? boost?) out there where the problem
has been addressed - what would I google for? Is there a name for this
problem class?
cheers,
Martin
Avoid pass by reference? Don't modify your arguments? Write plenty
of unit tests?
> On Jul 2, 10:58 am, "Martin T." <0xCDCDC...@gmx.at> wrote:
>> When writing new C++ code, what is the point of passing any (input)
>> parameter by value when passing by const reference will just work as
>> well? (Even and especially PODs, I would not do it with a complex type
>> anyway.)
>> (Given that in 90% of the code you will never want to modify the
>> parameter anyway.)
>
> References are implemented as pointers, so whenever you use a
> reference parameter in your functions, two readings are performed:
> value of a pointer to the passed variable and the variable itself.
> As copy-constructors of built-in types are cheap, it's usually more
> efficient to pass them by value. The same applies to STL iterators.
> For all other types passing by const reference is preferable.
Not necessarily. For example, try running the program below. With my
compiler it prints the following
default ctor
=== by reference ===
default ctor
copy ctor
swap
dtor
dtor
=== by value ===
default ctor
swap
dtor
=== done ===
dtor
which demonstrates that passing by reference can cause twice as many
copies as passing by value when the source is an rvalue. That can
really add up when the source is expensive to copy like a
vector<string>. If you're going to copy the argument anyway, you're far
better off passing it by value and swapping it into position. That
allows the compiler to elide the copy used to create the parameter value
(inside the function) when the argument (outside the function) is an
rvalue.
> ------------------------------------------------------------------------
--------------------------------------------------------------------------
Your original code follows. Somehow it's not getting quoted in the
reply. So I put it in by hand.
{ Our apologies. Somehow an article with attachments (even if only pure text)
slipped through both moderation and our posting software. -mod/aps }
>#include <utility>
> #include <iostream>
> struct trace
> {
> trace() { std::cout << "default ctor\n"; }
> trace( trace const& ) { std::cout << "copy ctor\n"; }
> trace& operator=( trace const& ) {
std::cout << "copy assign\n"; return *this; }
How is that implemented? Perhaps
trace &operator-(const trace &t) {
std::cout << "assign" << std::endl;
trace temp(t); // one copy ctor
swap(temp); // void trace::swap(trace &); not shown
return *this;
}
> ~trace() { std::cout << "dtor\n"; }
> friend void swap( trace& x, trace& y ) { std::cout << "swap\n"; }
> };
>
> template <class T>
> struct holder
> {
> void set_by_value(T x)
> {
> using std::swap;
> swap(x, held);
> }
>
> void set_by_reference(T const& x)
> {
> T tmp(x);
> using std::swap;
> swap(tmp, held);
> }
>
> private:
> T held;
> };
>
> int main()
> {
> holder<trace> h;
>
> std::cout << "=== by reference ===\n";
> h.set_by_reference( trace() );
> std::cout << "=== by value ===\n";
> h.set_by_value( trace() );
> std::cout << "=== done ===\n";
> }
>
>------------------------------------------------------------------------
> --------------------------------------------------------------------------
After I run this code with the changes I described, and a few other
minor ones, I get this output:
default ctor
=== by reference ===
default ctor
assign
copy ctor
trace::swap(trace &t)
dtor
dtor
=== by value ===
default ctor
copy ctor
assign
copy ctor
trace::swap(trace &t)
dtor
assign
copy ctor
trace::swap(trace &t)
dtor
dtor
dtor
=== done ===
dtor
Here is the code as I modified it:
#include <utility>
#include <iostream>
#include <string>
void m(const std::string &s) {
std::cout << s << std::endl;
}
struct trace
{
void swap(trace &t) {
m("trace::swap(trace &t)");
}
trace() {m("default ctor"); }
trace(const trace &) { m("copy ctor"); }
trace& operator=(const trace &t) {
m("assign");
trace temp(t);
swap(temp);
return *this;
}
~trace() { m("dtor"); }
};
template <class T>
struct holder
{
void set_by_value(T x)
{
std::swap(x, held);
}
void set_by_reference(const T &x)
{
held = x;
}
private:
T held;
};
void f()
{
holder<trace> h;
m("=== by reference ===");
h.set_by_reference( trace() );
m("=== by value ===");
h.set_by_value( trace() );
m("=== done ===");
}
int main() {
f();
}
Your compiler seems to be doing some nice optimizations for the value
version.
The output from your by value version is,
> === by value ===
> default ctor
> swap
> dtor
> === done ===
I'm a little curious as to what's happening to the instance of trace()
that you're passing to set_by_value.
I wonder what would happen if the code was a little different, maybe,
m("=== by value ===");
const trace t;
h.set_by_value( t );
// do something else with t here
m("=== done ===");
LR
> void f_ref_int(const int& x) {
> int y = x;
> y++;
> }
The number one reason against pass-by-const-reference here is not
technical: it looks funny, it is (I think it's safe to say) at best
pointless, and nobody else is doing it.
The way I react when I read someone's code and it contains odd
constructs which don't actually /do/ anything, is that I start to
distrust the rest of the code, too. Clearly, that hurts both me and
the author.
Sometimes, sticking to tradition is the best way.
/Jorgen
--
// Jorgen Grahn <grahn@ Ph'nglui mglw'nafh Cthulhu
\X/ snipabacken.se> R'lyeh wgah'nagl fhtagn!
> David Abrahams wrote:
> Your original code follows. Somehow it's not getting quoted in the
> reply. So I put it in by hand.
>
>
> { Our apologies. Somehow an article with attachments (even if only pure text)
> slipped through both moderation and our posting software. -mod/aps }
Mods: are you saying that my article should have been rejected because
of the inline text attachments? I'm not sure that's a great policy,
since inline plaintext is the only way to get some newsreaders not to
wrap lines, which tends to obfuscate the code.
>>#include <utility>
>> #include <iostream>
>
>> struct trace
>> {
>> trace() { std::cout << "default ctor\n"; }
>> trace( trace const& ) { std::cout << "copy ctor\n"; }
>
>
>> trace& operator=( trace const& ) {
> std::cout << "copy assign\n"; return *this; }
>
> How is that implemented? Perhaps
> trace &operator-(const trace &t) {
> std::cout << "assign" << std::endl;
> trace temp(t); // one copy ctor
> swap(temp); // void trace::swap(trace &); not shown
> return *this;
> }
Not if you want it to be efficient with rvalues. Instead:
trace& operator=(trace rhs) { swap(*this,rhs); return *this; }
>
>> ~trace() { std::cout << "dtor\n"; }
>> friend void swap( trace& x, trace& y ) { std::cout << "swap\n"; }
>> };
>>
<schnipp>
What conclusions can we draw from that?
> Your compiler seems to be doing some nice optimizations for the value
> version.
>
> The output from your by value version is,
>
>> === by value ===
>> default ctor
>> swap
>> dtor
>> === done ===
>
> I'm a little curious as to what's happening to the instance of trace()
> that you're passing to set_by_value.
It's an allowed optimization called "copy elision." Most modern
compilers do it (probably yours, even). The compiler is allowed to
eliminate any copying of an rvalue passed as a by-value argument and any
copying performed when returning by value from a function. Both
optimizations are done by allocating the storage for the argument/return
value in the caller's stack area.
> I wonder what would happen if the code was a little different, maybe,
>
> m("=== by value ===");
> const trace t;
> h.set_by_value( t );
> // do something else with t here
Now the argument is an lvalue; the compiler is forced to copy it.
--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
Would you make this statement if 'int' were replaced with 'Foo', where
Foo is a class or struct?
> The way I react when I read someone's code and it contains odd
> constructs which don't actually /do/ anything, is that I start to
> distrust the rest of the code, too. Clearly, that hurts both me and
> the author.
>
> Sometimes, sticking to tradition is the best way.
I just did a grep for such constructs in my pet project: 1726
occurences in 535 files.
What it does for me is let caller pass aggregates by computationally-
less-expensive reference with a contract that callee will be unable to
modified object being referred to. :)
How do you pass aggregates?
-Le Chaud Lapin-
--
One corner case for consideration:
typedef int File_Handle;
void fn(File_Handle fh); // (A) OR
void fn(const File_Handle& fh); // (B) OR
void fn(Traits<File_Handle>::Const_Param fh); // (C) ??
I would argue that (A) presents a maintenance issue - particularly if
the typedef is provided by a library header. Consider a change to a
"class File_Handler" in which copy construction and/or destruction
have unwanted side effects (e.g. badly implemented RAII), or some
large data is added (cache, buffer, ...?). The client code (A) might
keep compiling but not run as intended.
Implications of (B) are discussed elsewhere in thread, and would be my
general preference. Approach (C) is good but clumsy, verbose and
arguably confusing/obfuscating - more appropriate when useful to
ensure optimal library code, rather than for use by default in
applications.
Cheers,
Tony
Good point, but unfortunate example. The word "handle" in
"File_Handle" is intended to signal to the user "see this as some kind
of pointer or reference to a File". I think most people would expect
to be able to handle it as if it was a void *, or an index into some
array not available to the programmer.
/Jorgen
--
// Jorgen Grahn <grahn@ Ph'nglui mglw'nafh Cthulhu
\X/ snipabacken.se> R'lyeh wgah'nagl fhtagn!
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
No. Note the "here" in "reason against pass-by-const-reference here".
It refers to "const int&" in the example code above.
The original poster stated from the start that he was talking about
types like int, bool and so on. He, you, I and probably everyone else
agree that passing e.g. a std::string by const reference is a good
thing.
/Jorgen
--
// Jorgen Grahn <grahn@ Ph'nglui mglw'nafh Cthulhu
\X/ snipabacken.se> R'lyeh wgah'nagl fhtagn!
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
Quite so, and in general real IT references to "file handles" serve
this purpose with respect to OS-managed data. Still, consider
possible layering of convenience functions over this abstraction: a
programmer might want to "enrich" these handles with a class to, for
example, open a file and store the file handle, ensure the file handle
was closed in the destructor, support OO-style operations etc.. The
merits can be argued, but experience shows such classes do often get
written, and it's not unlikely the class would be named after the
handle abstraction it wraps. You obviously understand the issues, but
for the benefit of others: the danger in this case is only some
unintended file opens and closes and temporary use of an additional O/
S file handle resource (of which there are typically limited numbers),
but in some cases (e.g. when locks are involved), worse things like
deadlocks could occur....
Tony
--
> 2. Passing a paramater by value allows modification of the paramater as a
> local variable I.E.
>
> void Reverse( const char* CString, int length )
> {
> while ( --length )
> {
> std::cout << CString[length];
> }
> }
I know that this does not relate to the original question, but there's an
off-by-one error in your code. The first letter of the original string will
not be displayed, i.e. "Hello" turns to "olle" rather than "olleH". You
probably meant this:
void Reverse( const char* CString, int length )
{
while ( length-- )
{
std::cout << CString[length - 1];
}
}
--
Matthias Hofmann
Anvil-Soft, CEO
http://www.anvil-soft.com - The Creators of Toilet Tycoon
http://www.anvil-soft.de - Die Macher des Klomanagers