12

I know you pass by reference to a function in C++ when you want to change the value of the original variable. But you can also pass by reference when you want the program to be more efficient and if you don't want to change anything in the variable passed to the function, you just make it const. My question is, why not always have your functions accept variables passed by const reference if it is more efficient than just passing the variable and having the compiler create a new one within the scope of the function? To extend the question, what would be a case where a function WOULD need to copy a variable over passed through the parameters?

aanrv
  • 2,159
  • 5
  • 25
  • 37
  • 6
    It's not always more efficient. – chris Nov 14 '13 at 01:44
  • 2
    http://stackoverflow.com/questions/15600499/how-to-pass-parameters-correctly/15600615#15600615 great guide on passing params in c++, keep in mind c++ probably has more ways to pass params than any other language it's not easy to master – aaronman Nov 14 '13 at 01:44
  • It's a good question. However it must have been answered a million times. Try Effective C++, by Meyers. – Graham Perks Nov 14 '13 at 01:55
  • 1
    @aaronman: C++ only has two: pass by reference and pass by value. Many others (e.g., Java) have exactly the same (albeit with less control over which is used when). Ada and (if memory serves) Algol both have more. – Jerry Coffin Nov 14 '13 at 02:10
  • @JerryCoffin I should have phrased it differently, as for Algol and Ada I don't know enought to have an opinion – aaronman Nov 14 '13 at 02:13
  • You don't need to pass basic types by reference, as they relatively have no cost to be copied. Take a look at operator<< overloads in ostream class. There you can see operator<< is defined to get parameters of basic types as pass by value. – Kasra Jan 04 '17 at 12:19

3 Answers3

8

When an argument is passed by value it is modifiable and copying it may be elided. For example, the canonical way to implement the assignment operator looks like this:

T& T::operator= (T value) {
    value.swap(*this);
    return *this;
}

At first sight it may look inefficient because a T is being copied. However, it would be copied anyway, i.e., if a copy is needed one will be created either way:

T& T::operator= (T const& value) {
    T(value).swap(*this); // has to do a copy right here
    return *this;
}

However, for the first version, it may be possible not to create copy at all, for example

T f() { return T(); }
// ...
T x = ...;
x = f();

When assigning the result of f() which is of type T to x the compiler may decide that it doesn't need to copy the result of f() and instead pass it into the assignment operator directly. In that case, if the assignment operator takes the argument by const& the compiler has to create a copy inside the assignment operator. In the implementation taking the argument by value it can elide the copy! In fact, the return from f() can already elide the copy, i.e., the call to f() and the following assignment may just involve the default construction of the object! ... and for many modern compilers that is, indeed, the case!

Put differently: if you need to copy an argument, getting it passed by value may avoid the need to create a copy. Also, you can std::move() from value arguments but not from const& arguments.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
4

But you can also pass by reference when you want the program to be more efficient and if you don't want to change anything in the variable passed to the function, you just make it const.

It's wrong. Passing the arguments of basic types (int, float, char....) is more effecient than passing them by reference. const & is more effecient in passing the BIG OBJECT. Because the reference is a alias of a object in essential, the compiler need to handle more information.

xxx7xxxx
  • 774
  • 6
  • 18
2

References are essentially a special contract over pointers the payback for which is some syntactic sugar and simplicity. Within a function body, the compiler may be at liberty to eliminate references, but when they are passed as arguments what actually gets passed is a pointer.

The upshot is that using a reference may incur derference costs.

void func(int& i) {
    int j = i; // secretly a dereference
    // ...
}

incurs the same overheads as

void func(int* i) {
    int j = *i;
    // ...
}

Local, convenience references can often be optimized out, but reference arguments have to be dereferenced at least the first time they are used.

kfsone
  • 23,617
  • 2
  • 42
  • 74
  • This may slow down the optimiser, but any modern compiler will be able to optimise a pointer just as well as a variable. Besides, most of the time you just point to a variable on the stack, making `const int j = i` just as fast as `int const &j = i;`, but it consumes `sizeof(int*)` bytes instead of `sizeof(int)`, which is actually a advantage for larger types. – yyny Apr 21 '16 at 15:29