138
void DoWork(int n);
void DoWork(const int &n);

What's the difference?

nbro
  • 15,395
  • 32
  • 113
  • 196
Pirate for Profit
  • 1,591
  • 2
  • 14
  • 16

8 Answers8

143

The important difference is that when passing by const reference, no new object is created. In the function body, the parameter is effectively an alias for the object passed in.

Because the reference is a const reference the function body cannot directly change the value of that object. This has a similar property to passing by value where the function body also cannot change the value of the object that was passed in, in this case because the parameter is a copy.

There are crucial differences. If the parameter is a const reference, but the object passed it was not in fact const then the value of the object may be changed during the function call itself.

E.g.

int a;

void DoWork(const int &n)
{
    a = n * 2;  // If n was a reference to a, n will have been doubled 

    f();  // Might change the value of whatever n refers to 
}

int main()
{
    DoWork(a);
}

Also if the object passed in was not actually const then the function could (even if it is ill advised) change its value with a cast.

e.g.

void DoWork(const int &n)
{
    const_cast<int&>(n) = 22;
}

This would cause undefined behaviour if the object passed in was actually const.

When the parameter is passed by const reference, extra costs include dereferencing, worse object locality, fewer opportunities for compile optimizing.

When the parameter is passed by value an extra cost is the need to create a parameter copy. Typically this is only of concern when the object type is large.

Bondolin
  • 2,793
  • 7
  • 34
  • 62
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 3
    Could you please explain what's the penalty when you mention _worse object locality_ in your answer? – Zoso Mar 01 '21 at 20:43
  • 1
    Probably https://en.wikipedia.org/wiki/Locality_of_reference. TLDR when commonly accessed parts of memory are grouped together you can get better performance as a result of caching internals. Another way you can influence this is with GCC attributes `hot` and `cold`. –  Jun 09 '21 at 04:17
  • 1
    Why does passing by const reference lead to _worse object locality_? – rohitjv Oct 09 '21 at 18:00
  • 1
    @rohitjv the referenced object may lie anywhere in memory. If you pass by value, you have the entire object (a copy) right in your current stack frame, i. e. in the memory region your function "lives" in, which is as _local_ as it gets. – Reizo Mar 20 '22 at 00:28
124

The difference is more prominent when you are passing a big struct/class:

struct MyData {
    int a,b,c,d,e,f,g,h;
    long array[1234];
};
void DoWork(MyData md);
void DoWork(const MyData& md);

When you use use 'normal' parameter, you pass the parameter by value and hence creating a copy of the parameter you pass. If you are using const reference, you pass it by reference and the original data is not copied.

In both cases, the original data cannot be modified from inside the function.


EDIT:
In certain cases, the original data might be able to get modified as pointed out by Charles Bailey in his answer.

horro
  • 1,262
  • 3
  • 20
  • 37
Afriza N. Arief
  • 7,696
  • 5
  • 47
  • 74
  • 2
    Does passing by ref require `const`? I mean wouldn't your answer apply to the case where we were comparing just 'normal' param with ref param? – gonidelis Jan 31 '21 at 15:46
  • 7
    Passing by ref doesn't require `const` of course.. it's just that by having `const`, the compiler will help you to avoid modifying the original data.. but this can be intentionally bypassed as pointed out by Charles Bailey.. – Afriza N. Arief Feb 01 '21 at 03:39
56

There are three methods you can pass values in the function

  1. Pass by value

    void f(int n){
        n = n + 10;
    }
    
    int main(){
        int x = 3;
        f(x);
        cout << x << endl;
    }
    

    Output: 3. Disadvantage: When parameter x pass through f function then compiler creates a copy in memory in of x. So wastage of memory.

  2. Pass by reference

    void f(int& n){
        n = n + 10;
    }
    
    int main(){
        int x = 3;
        f(x);
        cout << x << endl;
    }
    

    Output: 13. It eliminate pass by value disadvantage, but if programmer do not want to change the value then use constant reference

  3. Constant reference

    void f(const int& n){
        n = n + 10; // Error: assignment of read-only reference  ‘n’
    }
    
    int main(){
        int x = 3;
        f(x);
        cout << x << endl;
    }
    

    Output: Throw error at n = n + 10 because when we pass const reference parameter argument then it is read-only parameter, you cannot change value of n.

Hu Xixi
  • 1,799
  • 2
  • 21
  • 29
sneha
  • 819
  • 7
  • 7
  • 2
    Do you mean to have the print statement in main(), after f() is called? Because with the print statement inside f(), I think the output of the first example is wrong: it should be 13, no? – Seb Apr 20 '17 at 18:21
  • No @Seb you aren't correct. The output will be 3 itself. This is what it means to *pass by value*. The **value** of `x` is being passed, namely `3` and not `x` itself. What you are talking about is pass by reference, ie, the 2nd example... – Sabito stands with Ukraine Aug 02 '20 at 05:48
  • Hm. Yeah, seems correct now. My comment was over 3 years ago. Maybe it was edited? (If not, sorry. (: ) – Seb Aug 03 '20 at 12:57
10

With

 void DoWork(int n);

n is a copy of the value of the actual parameter, and it is legal to change the value of n within the function. With

void DoWork(const int &n);

n is a reference to the actual parameter, and it is not legal to change its value.

Avi
  • 19,934
  • 4
  • 57
  • 70
7

Since none of you mentioned nothing about the const keyword...

The const keyword modifies the type of a type declaration or the type of a function parameter, preventing the value from varying. (Source: MS)

In other words: passing a parameter by reference exposes it to modification by the callee. Using the const keyword prevents the modification.

Aoi Karasu
  • 3,730
  • 3
  • 37
  • 61
1

The first method passes n by value, i.e. a copy of n is sent to the function. The second one passes n by reference which basically means that a pointer to the n with which the function is called is sent to the function.

For integral types like int it doesn't make much sense to pass as a const reference since the size of the reference is usually the same as the size of the reference (the pointer). In the cases where making a copy is expensive it's usually best to pass by const reference.

Andreas Brinck
  • 51,293
  • 14
  • 84
  • 114
  • 3
    size isn't the only issue. Passing by const reference allows changes to the original object to be visible inside the function. Passing by value isolates the function body from such changes. – CB Bailey Apr 13 '10 at 05:33
  • @Charles Certainly, which indeed might be beneficial in some cases (and detrimental in some others ;) ). – Andreas Brinck Apr 13 '10 at 05:37
  • You can observe different behavior in multi-threaded environment when passing types like **int** by value vs by reference. – Aoi Karasu Apr 13 '10 at 05:55
0

Firstly, there is no concept of cv-qualified references. So the terminology 'const reference' is not correct and is usually used to describle 'reference to const'. It is better to start talking about what is meant.

$8.3.2/1- "Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef (7.1.3) or of a template type argument (14.3), in which case the cv-qualifiers are ignored."

Here are the differences

$13.1 - "Only the const and volatile type-specifiers at the outermost level of the parameter type specification are ignored in this fashion; const and volatile type-specifiers buried within a parameter type specification are significant and can be used to distinguish overloaded function declarations.112). In particular, for any type T, “pointer to T,” “pointer to const T,” and “pointer to volatile T” are considered distinct parameter types, as are “reference to T,” “reference to const T,” and “reference to volatile T.”

void f(int &n){
   cout << 1; 
   n++;
}

void f(int const &n){
   cout << 2;
   //n++; // Error!, Non modifiable lvalue
}

int main(){
   int x = 2;

   f(x);        // Calls overload 1, after the call x is 3
   f(2);        // Calls overload 2
   f(2.2);      // Calls overload 2, a temporary of double is created $8.5/3
}
Winter
  • 3,894
  • 7
  • 24
  • 56
Chubsdad
  • 24,777
  • 4
  • 73
  • 129
-1

Also, you can use the const int& x to initialize it with r-value and this will cause that you can't change x or bind it with another values.

const int& x = 5; // x is a constant reference to r-value 5
x = 7;            // expression is not a modifable value
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 31 '23 at 15:30