7

I read that they are conceptually equal. In practice, is there any occasion that

foo(T t) 

is preferred over

foo(const T& t)

? and why?


Thanks for the answers so far, please note I am not asking the difference between by-ref and by-val.

Actually I was interested in the difference between by-const-ref and by-val.

I used to hold the oipinion that by-const-ref can replace by-value in call cases since even Herb Sutter and Bjarne said they are conceptually equal, and "by ref"(be it const) implies being faster. until recently, I read somewhere that by-val may be better optimized in some cases.

Then when and how?

t.g.
  • 1,719
  • 2
  • 14
  • 25

15 Answers15

13

Built-in types and small objects (such as STL iterators) should normally be passed by value.

This is partly to increase the compiler's opportunities for optimisation. It's surprisingly hard for the compiler to know if a reference parameter is aliasing another parameter or global - it may have to reread the state of the object from memory a number of times through the function, to be sure the value hasn't changed.

This is the reason for C99's restrict keyword (the same issue but with pointers).

James Hopkin
  • 13,797
  • 1
  • 42
  • 71
  • Yes, a pointer (or reference for that matter) may require more memory than a POD such as for example an unsigned char or short. This is an important consideration in certain environments (think embedded), by additionally marking the variable as 'const', you are communicating to the compiler that you don't need to modify it in your function, so that the compiler knows that you don't intend to modify the variable, neither externally nor internally. – none May 22 '09 at 16:56
  • In most environments, types smaller than the machine's register size (such as char and short on x86) are padded out to that size (e.g. 32 bits). I suppose there could be highly specialized embedded systems where that's not true, but on most machines, no memory is saved by using a char instead of an char*. – Adam Rosenfield May 22 '09 at 17:07
  • @Jame: Thanks, can you please elaborate what optimization could be done in your said cases? I was suspecting so, but could not figure it out myself. – t.g. May 23 '09 at 01:21
5

If you want to locally modify t (without affecting the original) in the body of your method (say in the process of calculating something), the first method would be preferential.

Paul Sonier
  • 38,903
  • 3
  • 77
  • 117
  • but why, since they are conceptually identical? – t.g. May 22 '09 at 16:45
  • 5
    the const specifier will prevent modifications and thus makes it obvious to the compiler that you don't intend to mutate that variable. – none May 22 '09 at 16:53
2

If the object being passed is a smart pointer (i.e. it does its own reference counting) then passing by value might be more reasonable.

I realize that's sort of a sideways answer to your question - the object wrapped by the smart pointer is not copied when passed by value, so it's more similar to passing by reference in that case. Nevertheless, you don't need "by reference" semantics in this case.

There is a problem with my line of reasoning so far, though - you'll lose the "const-ness" of the argument by passing by value. Perhaps you should just use the "by reference" semantics after all...

Matt Dillard
  • 14,677
  • 7
  • 51
  • 61
  • 2
    Smartpointers often require a lock when copied - value semantics on smart pointers are a perfo nightmare, – xtofl May 22 '09 at 17:45
  • +1 @xtofl Copying smart pointers can be a huge performance lose. Pass them by const reference like any other expensive-to-copy type. – Bklyn May 25 '09 at 01:40
2

Don't forget that there are cases where there is a difference - when you're dealing with objects that have strange copy/assignment semantics.

auto_ptr<> is the classic example - pass those around by value without thinking about the consequences and you may end up with a mess.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
1

In addition, foo(T t) is normally used when T is a simple type (int, bool, etc).

no-op
  • 131
  • 4
1

Another case that hasn't been mentioned is heavy use of the object. Let's say you pass a struct with 5 ints as members. If you are going to be accessing all 5 quite a lot in your function, there comes a point when the dereference cost outweighs the copy cost. However, you'd have to run a profiler to know when that is.

I should point out, though, that things like STL containers, which allocate memory on the heap, should almost never be passed by value if avoidable, because heap allocation is very slow compared to stack allocation.

rlbond
  • 65,341
  • 56
  • 178
  • 228
  • The dereferencing cost is identical in both cases. If you're using the struct members frequently, the struct pointer will almost certainly be stored in a register, so its members will be accessed through things like 8(%edx), versus things like -8(%ebp) for local variables on x86. – Adam Rosenfield May 22 '09 at 17:09
1

Here is a case when you don't have other option but only to pass a parameter by value. Of course boost does handle this. But without boost we have to pass a Value to function by value.

class Test
{
public:
    Test()
    {
        std::set<std::string> values;
        values.insert("A");
        values.insert("V");
        values.insert("C");

        std::for_each(values.begin(), values.end(), 
                bind1st(mem_fun(&Test::process), this));
    }

private:
    void process( std::string value )
    {
        std::cout << "process " << value << std::endl;
    }
};
Mykola Golubyev
  • 57,943
  • 15
  • 89
  • 102
1

The reason pass by const reference and by value are conceptually the same is that neither can modify the original.

Normally, I am big fan of pass by value because it creates code that avoids many of the complexities that occur when multiple threads are sharing access to common data.

That said, it does potentially make you're code slower. My take in the past has always been to prefer pass by value unless I know their is (or will be) a performance problem by doing so. I may have to modify this slightly to include pass by const reference as an even better option.

dicroce
  • 45,396
  • 28
  • 101
  • 140
1

If the most straightforward implementation of the function involves modifying the parameter value locally, then it makes sense to pass it by value rather than by const reference

For example, the one line version of strcpy:

char *strcpy(char *dest, const char *src)
{
   while (*dest++ = *src++);

   return s1;
}

If you took in the pointers as const references, you would need to copy them to temporaries in the body of your program, rather than letting the paramater passing mechanism do it for you.

JohnMcG
  • 8,709
  • 6
  • 42
  • 49
1

Two very specific cases:

When you are writing assignment operators with strong exception guarantees you can either write them like

X& X::operator=(const X& orig)
{
    X tmp(orig);
    swap(this, tmp);
    return *this;
}

or you can recognize that the first thing that happens is that you make a copy and just have it done as part of the call

X& X::operator=(X tmp)
{
    swap(this, tmp);
    return *this;
}

If you have a smart pointer with ownership semantics, e.g. auto_ptr, and you want to transfer ownership to the called function you should pass it by value. Of course 8 people will now quickly point out that you probably don't want to use auto_ptr and they're probably right, but sometimes you don't make that choice.

While not at all specific, I frequently end up passing smallish objects around by value when that saves me an allocation on the heap. Not only does the actual allocation and eventual deallocation take time but referencing everything through a pointer does nothing to improve your data locality. It might in other words make a difference to your performance. Exactly where the break-even point is will depend on your application, but I would personally don't hesitate to pass an object that is a few pointer sizes large.

j2.
  • 66
  • 2
1

They're not conceptually equal at all...

The former creates a copy of the object inside the function. This implies that the value can be modified in the function safely. It also implies that a full copy of the object occurred, which can be a problem if the object is large.

The latter creates an alias to the object and states that it cannot be modified within the object. No copying occurs, but each access to the object inside the function will require a dereference. The compiler takes care of this for us, but it's still important to know.

The difference becomes very important if you have a type that is normally passed in registers. For example, integers, floating point numbers, and even 4-float vectors on some platforms. Performance concerns dictate that you want to object to stay in a register for as long as possible without writing itself back to memory, and pass by value makes this much more likely.

So for basic types (char, short, int, long, float, double), you should always prefer pass by value unless you specifically need to use a reference to store a value for use after the function exits. For full objects, generally prefer to pass by const reference.

Dan Olson
  • 22,849
  • 4
  • 42
  • 56
0
  1. As previously noted, prefer pass-by-value if you want a copy of the object in your function.
  2. I usually use pass-by-value if copying T is cheaper than creating/copying a reference, e.g. T=char, T=short. The benefit here could be platform dependent, and you'd probably still want const where applicable to help the optimizer.
Ryan
  • 567
  • 4
  • 15
0

Some routines require a copy, and so should not be pass by reference. For example, a chess playing program's move generator might need a copy of the current position to work on (recursively) rather than actually modifying the original instance of the position.

  • I'd still prefer the pass by const ref. The called code might have code paths that don't need the copy. They can operate on the const ref and return without the overhead of the copy. Then other code paths in the function could create a copy when they discover they need to. – jmucchiello May 22 '09 at 17:29
0

Answered in this answer. This is a SymLink to that answer. Please vote there, if anywhere :)

Community
  • 1
  • 1
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
0

Boost.CallTraits is a lesser-known but helpful facility for doing parameter and result passing with what should be the most efficient method available for the type in question.

Bklyn
  • 2,559
  • 2
  • 20
  • 16