John
John

Reputation: 4706

C++ pass by reference for large objects

If I have a C++ function declaration:

int func(const vector<int> a)

Would it always be beneficial to replace it with

int func(const vector<int> &a)

since the latter does not need to make a copy of a to pass into the function?

Upvotes: 3

Views: 2920

Answers (6)

Nikos C.
Nikos C.

Reputation: 51910

Correct. Passing a reference will avoid a copy. You should make use of references when there's a copy involved and you don't actually need one. (Either because you don't intent to modify the value, in which case operating on the original is fine and you'd use a const reference, or because you do want to modify the original rather than a copy of it, in which case you'd use a non-const reference.)

This isn't limited to function arguments of course. For example, look at this function:

std::string foo();

Most people would use that function in this way:

std::string result = foo();

However, if you're not modifying result, this is way better:

const std::string& result = foo();

No copy is being made. Also, contrary to pointers, a reference guarantees that the temporary returned by foo() stays valid and will not go out of scope (a pointer to a temporary is dangerous, while a reference to a temporary is perfectly safe.)

The C++-11 standard solves this problem by using move semantics, but most existing code doesn't make use of this new feature yet, so using references wherever possible is a good habit to get into.

Also, note that you have to be careful about temporary lifetimes when binding temporaries to references, e.g.:

const int& f(const int& x)
{ return x; }

const int& y = f(23);
int z = y; /* OOPS */

The point being that the lifetime of the temporary int with value 23 doesn't extend beyond the end of the expression binding f(23) to y, so the attempt to assign y to z results in undefined behavior (due to the dangling reference).

Note that when you're dealing with POD types (Plain Old Data), like int or char, you don't win anything by avoiding a copy. Usually a reference is just as big as an int or long int (usually as big as a pointer), so copying an int by reference is the same as copying the int itself.

Upvotes: 0

Sebastian
Sebastian

Reputation: 1889

Some reasons I can imagine the pass by value could be more efficient:

  • It can be better paralellized. Because there's no aliasing. The original can change without affecting the value inside the function.
  • Better cache locality

Upvotes: 0

GManNickG
GManNickG

Reputation: 504133

In terms of efficiency like you're thinking, almost always yes. There are times where (purportedly) this may be slower, typically with types that are fundamental or small:

// copy x? fits in register: fast
void foo(const int x);

// reference x? requires dereferencing on typical implementations: slow
void foo(const int& x); 

But with inlining this doesn't matter anyway, plus you can just type it by-value yourself; this only matters with generic template functions.

However it's important to note that your transformation may not always be valid, namely because your function gets its own copy of the data. Consider this simpler example:

void foo(const int x, int& y)
{
    y += x;
    y += x;
}

int v = 1;
foo(v, v); // results in v == 3

Make your transformation and you get:

void foo(const int& x, int& y)
{
    y += x;
    y += x;
}

int v = 1;
foo(v, v); // results in v == 4

Because even though you cannot write to x, it can be written to through other means. This is called aliasing. While probably not a concern with the example you've given (though global variables could still alias!), just be wary of the difference in principle.

Lastly, if you're going to make your own copy anyway, just do it in the parameter list; the compiler can optimize that for you, especially with C++11's rvalue references/move semantics.

Upvotes: 4

hmakholm left over Monica
hmakholm left over Monica

Reputation: 23342

Mostly it would be more efficient -- but if it happens that func needs to make its own copy of the vector and modify it destructively while it does whatever it does anyway, then you might as well save a few lines and let the language make the copy for you implicitly as a pass-by-value parameter. It is conceivable that the compiler might then be able to figure out that the copying can be omitted if the caller is not actually using its copy of the vector afterwards.

Upvotes: 2

Kerrek SB
Kerrek SB

Reputation: 477434

In short, yes. Since you can't modify a anyway, all your function body could do is make another copy, which you can just as well make from a const-reference.

Upvotes: 0

Jonathon Reinhart
Jonathon Reinhart

Reputation: 137497

In general, yes. You should always pass large objects by reference (or pass a pointer to them, especially if you are using C).

Upvotes: 4

Related Questions