Pass by value or const reference?

Started by
5 comments, last by Basiror 19 years, 7 months ago
For small objects, is it better to pass by value or by const reference? I am particularly interested in the following situations (assume 32-bit machine): 1. Passing a 32-bit value (ex. int) to a non-inlined function 2. Passing a 32-bit object (ex. class) to a non-inlined function 3. Passing a 64-bit value (ex. unsigned long long) to a non-inlined function 4. Passing a 32-bit value (ex. int) to an inlined function 5. Passing a 32-bit object (ex. class) to an inlined function 6. Passing a 64-bit value (ex. unsigned long long) to an inlined function What is the correct way to handle those situations? There are a number of factors, such as the amount of data being copied, two copies vs. copy and dereference, aliasing, creating temporaries, and so on, and I could use some help in sorting it all out. A couple of specific questions: Does passing by const reference have the aliasing problem? It seems like the optimizing compiler might be able to treat a const reference as being passed by value. Is this the case?
Advertisement
In all of the cases for a 32-bit parameter, pass by value is preferred. A pointer size is 32bits as well so why involve dereferencing at all.

For 64-bits Pass by const reference should be best, but wont be much different from pass by value. It'll probably just go on the stack anyway rather than in registers.

You can create aliasing anytime you pass by reference. It's not really a problem as such, the compiler will just take a pessimistic approach to make sure your code works. Yes pass-by value prevents aliasing, as does making a local copy of the reference before using it. Either way you're only doing what the compiler would otherwise do itself. Damned if you do, damned if you don't.

Don't worry about these kinds of things, they're only important if you know they're bottlenecks.

consider this:
int x=1;void f(int &a){  x += 4;  a = a*2;  a += x;}int main(){  cout << f(x); //should be 20.  return 0;}
That is aliasing and the compiler can't do squat about it.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
This is all compiler dependant but for trival cases the compiler can optimize references (but not pointers) and actually use the value passed to it and no copying of any kind occurs not even a copy of address this is most likely with functions which are inlined, a non-trivial example would be using polymorphic behaviour through a reference of a base type then its mostly likely that the compiler will generate code simillar or the same as pointers.

For built-in primivites types the most pridictable solution is to just pass by value, for user-defined type that are or simlliar to smart pointers and iterators which are small & fixed sized objects roughly the size of a pointer to the type in question again just pass-by value for a pridictable solution across all compilers, anything larger pass-by-reference unless of-course you need to modifiy the actual instance passed to the function then you'll need to pass-by-reference regardless.

[Edited by - snk_kid on September 24, 2004 5:24:55 PM]
Quote:Original post by iMalc
Don't worry about these kinds of things, they're only important if you know they're bottlenecks.

This deserves to be repeated, and emphisized.
Quote:Original post by iMalc
void f(int &a)
cout << f(x); //should be 20.

That shouldn't compile, amigo. Last I checked, ostream & operator<<( ostream & , void ) had yet to be implemented.
Quote:Original post by Russell
1. Passing a 32-bit value (ex. int) to a non-inlined function
2. Passing a 32-bit object (ex. class) to a non-inlined function
3. Passing a 64-bit value (ex. unsigned long long) to a non-inlined function
4. Passing a 32-bit value (ex. int) to an inlined function
5. Passing a 32-bit object (ex. class) to an inlined function
6. Passing a 64-bit value (ex. unsigned long long) to an inlined function

1/2: shouldn't differ, should pass by value (since on a 32 bit system you'd be passing 32 bit values anyways)
3: I'm guessing value again, try compiling the function and check the generated function assembly.
4/5: shouldn't differ, and it shouldn't matter on a smart compiler - although const reference wouldn't hurt, just to let the compiler know it dosn't need to make a local copy ahead of time.
6: smart compiler shouldn't make a difference - again, const reference wouldn't hurt.

4/5/6: I'm assuming it actually gets inlined, even though the keyword only states a suggestion which need not be implemented by your compiler.
Quote:Original post by snk_kid
for user-defined type that are or simlliar to smart pointers and iterators which are small & fixed sized objects roughly the size of a pointer to the type in question again just pass-by value for a pridictable solution across all compilers, anything larger pass-by-reference unless of-course you need to modifiy the actual instance passed to the function then you'll need to pass-by-reference regardless.


Actually, even for many small user-defined object types, particularly smart pointers, I still would recommend passing by reference to const. The reason why is because unless your type has a trivial copy constructor, you'll save on the operation of something such as increasing the reference count of a passed smart pointer, and as well, decreasing the count upon the function's exit. This is something that cannot be optimized away. In general, I'd recommend passing just about any user-defined objects by reference to const unless they are both relatively small and also have trivial copy constructors and destructors (a little over 32 bits on a 32 machine is acceptable, I'd say stop after about 128 bits, but that's just an estimated guess and depends on how much you access the object -- check with tests if you're really concerned).

Again though, in general, you really shouldn't worry too much about this stuff unless it's a time critical situation.
Quote:Original post by Polymorphic OOP
Actually, even for many small user-defined object types, particularly smart pointers, I still would recommend passing by reference to const. The reason why is because unless your type has a trivial copy constructor, you'll save on the operation of something such as increasing the reference count of a passed smart pointer, and as well, decreasing the count upon the function's exit. This is something that cannot be optimized away. In general, I'd recommend passing just about any user-defined objects by reference to const unless they are both relatively small and also have trivial copy constructors and destructors (a little over 32 bits on a 32 machine is acceptable, I'd say stop after about 128 bits, but that's just an estimated guess and depends on how much you access the object -- check with tests if you're really concerned).


Important point i missed, but iterators you can almost always assume trival copy constructor & small instance so you can pass that by value, also if you have method/function thats templated for an iterator range this gives the flexibility to use raw pointers for C-arrays aswell.

Quote:Original post by Polymorphic OOP
Again though, in general, you really shouldn't worry too much about this stuff unless it's a time critical situation.


agreed [smile]
here is a complex overview about C/C++ optimizations

http://www.tantalon.com/pete/cppopt/main.htm
http://www.8ung.at/basiror/theironcross.html

This topic is closed to new replies.

Advertisement