"const int&" vs "const int" as parameters

Hello I have one question.
On the code we receive from the Professor he sometimes uses as parameter:
const int&
or
const T&

As far as I know, const will not allow any modification to any function. And, as a consequence, I do not understand why one would need the ampersand, if no variable can be passed.
Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  
void x::duplicate_constamp (const int& a,const int& b, const int& c)
//here const with int and ampersand
{        cout<<endl;
cout<<"duplicate_constamp (const int& a,const int& b,const int& c):"<<endl;

    cout<<"a*2= "<<a*2<<endl;
    cout<<"b*2= "<<b*2<<endl;
    cout<<"c*2= "<<c*2<<endl;
/*
    a=a*2;
    b=b*2;
    c=c*2;*/
//i need to comment that out because otherwise the program does not compile
}
void x::duplicate_const (const int a,const int b, const int c)
//here is the same, without ampersand
{        cout<<endl;
cout<<"duplicate_const (const int a,const int b,const int c):"<<endl;

    cout<<"a*2= "<<a*2<<endl;
    cout<<"b*2= "<<b*2<<endl;
    cout<<"c*2= "<<c*2<<endl;
/*
    a=a*2;
    b=b*2;
    c=c*2;*/
//and i cannot assign it here either
}


If there is any difference, which would be the difference between "const T&" as parameter and "const T"?

Thanks in advance
Passing parameters by reference does not create a copy, like the by value does. If objects of type T are huge -- expensive to copy -- then passing by reference is more efficient.

An int is cheap to copy, but std::vector<std::vector<std::vector<int>>> is not.
If you want to pass by ref to avoid an expensive copy, then if the function doesn't/shouldn't modify the contents then it's usual to pass as a const ref. This means that also r-values (eg literals) can also be passed which couldn't if passed as just a ref (you can't take a ref of an r-value).

As refs are usually implemented by pointers (using a copy of a pointer) then for performance if the passed data size is greater than the size of a pointer (either 32 or 64 bits depending), then pass by ref else pass by value.
Thank you, I understood that:
1 Passing by "const &" does not create a copy and passing by "const value" does.
2 Passing by "const &" is more efficient because it accepts a bigger value than passing by "const value"

Can someone please explain in point 1, where can one see a "copy" if one uses "const &" ? I do not understand why a copy would be needed if the value in the end will not change because the "const" is there.

An explanatory code I mean, so that the difference could be clear.
By default, all parameters passed to a function are passed 'by value'. This is how the C++ language is defined. Passing by value does a copy of the parameter. Changes to that value done within the function are not reflected in the value in the calling function. If you want changes in the value made in the function to be reflected back in the calling function then the parameter is passed by ref (or by pointer from c). Changes made in the function are reflected in the calling function. No copy is done. If you don't want a copy to be done and don't want changes to be made, then use const ref.

by value - int a - value of a is copied and the copy can be changed. Changes are not reflected back in the calling function

by constant value - const int a - value of a is copied and the copy can not be changed

by ref - int& a - value of a is passed by ref. No copy is done and changes are reflected back in the calling function. The passed value in the calling function must be capable of being changed (so no literals, numbers etc)

by constant ref - const int& a - value of a is passed by ref. No copy is done and the value cannot be changed. The passed value in the calling function can be a literal, number etc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>

void funa(int z)
{
	z = 3;	// OK change to a is local to funa()
}

void funb(const int z)
{
	//z = 4;	//Compile error - trying to change a const
}

void func(int& z)
{
	z = 4;	// Change to a is reflected back to caller - so must be assignable
}

void fund(const int& z)
{
	// z = 4	//Compile error - trying to change a const
}

int main()
{
	int a = 1;

	funa(a);	// OK funa() gets the value 1
	funa(2);	// OK funa() gets the value 2

	funb(a);	// OK funb() gets the value 1
	funb(3);	// OK funb() gets the value 3

	func(a);	// OK func() gets the value 1 and the value of a in main() is 4 after
	//func(3);	// Compile error can't pass number to a ref

	fund(a);	// OK fund() gets the value 4
	fund(5);	// OK fund() gets the value 5
}


Hello Mustermann,


As far as I know, const will not allow any modification to any function. And, as a consequence, I do not understand why one would need the ampersand, if no variable can be passed.


Not quite correct. In the first part "function" should say "variable". The "const" in the parameters will not allow any of the variables to be changed, but does not affect the function.

keskiverto and seeplus have explained pass by value and pass by reference.


2 Passing by "const &" is more efficient because it accepts a bigger value than passing by "const value"


Again not entirely true. Passing an "int" to a function is only 4 bytes no mater the size of the number.So passing by reference does not have any noticeable advantage.

What they are more referring to is variables like a "std::string", "std::vector" or other STL containers. Because of the way these handle memory when they are created and used making a copy is more expensive in time and memory. So they are better passed by reference. Making it a "const" just means that you can use the variable in the function, but can not modify the variable.

I offer this as a suggestion because your code is not easy to read:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void x::duplicate_constamp(const int& a, const int& b, const int& c)
//here const with int and ampersand
{
    cout << '\n';
    cout << "duplicate_constamp (const int& a,const int& b,const int& c):" << '\n';

    cout
        << "a*2= " << a * 2 << '\n'
        << "b*2= " << b * 2 << '\n'
        << "c*2= " << c * 2 << '\n';
/*
    a=a*2;
    b=b*2;
    c=c*2;
*/
//i need to comment that out because otherwise the program does not compile
}

void x::duplicate_const(const int a, const int b, const int c)
//here is the same, without ampersand
{
    cout << '\n';
    cout << "duplicate_const (const int a,const int b,const int c):" << '\n';

    cout << "a*2= " << a * 2 << '\n';
    cout << "b*2= " << b * 2 << '\n';
    cout << "c*2= " << c * 2 << '\n';
/*
    a=a*2;
    b=b*2;
    c=c*2;
*/
//and i cannot assign it here either
}

Lines 7 - 10 show you an alternative way of using the "cout" to chain all 3 of your lines into 1 statement.

An example of using strings to display a menu:
1
2
3
4
5
6
7
std::cout <<
    "1. Menu item 1\n"
    "2. Menu item 2\n"
    "3. Menu item 3\n"
    "4. Menu item 4\n"
    " Enter choice: ";
std::cin >> choice;

Here only 1 insertion operator is needed and each line being a quoted string is combined into 1 large string before it is displayed. It does have the advantage to see what you output will look like and it makes changes or edits much easier.

The part with the commented code in each example the compiler is telling you that you are trying to change a variable that can not be changed.

Andy
Thank you Andy and Seeplus for your help.
My question may not be very clear, because it did not get answered yet.
My question is, on the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
duplicate_constref(const int& a, const int& b, const int& c)
//here const with int and ampersand
{
    cout << '\n';
    cout << "duplicate_constamp (const int& a,const int& b,const int& c):" << '\n';

    cout
        << "a*2= " << a * 2 << '\n'
        << "b*2= " << b * 2 << '\n'
        << "c*2= " << c * 2 << '\n';
/*
    a=a*2;
    b=b*2;
    c=c*2;
*/
//i need to comment that out because otherwise the program does not compile
}
void x::duplicate_const(const int a, const int b, const int c)
//here is the same, without the reference
{
    cout << '\n';
    cout << "duplicate_const (const int a,const int b,const int c):" << '\n';

    cout << "a*2= " << a * 2 << '\n';
    cout << "b*2= " << b * 2 << '\n';
    cout << "c*2= " << c * 2 << '\n';
/*
    a=a*2;
    b=b*2;
    c=c*2;
*/
//and i cannot assign it here either
}


duplicate_constref passes a variable by "const reference" and duplicate_const passes a value by "const value".

Keskiverto and Seeplus said that using "const reference" does not create a copy and "const value" does create a copy.

1 Where could one see a "copy" if one uses "const value" ?
I do not understand why a copy would be needed if the value in the end will not change because the "const" is there.

I just want to understand why the professor uses const reference and not just const (I do not understand why he would need to put const reference if he is not using super big variables like a vector of vectors, like Keskiverto said).

2 Does he need to write on the above code "const reference" or it is a waste of time that gives no changes and one could simply write "const value" and have the same result?

3 Would have the above example any significant change if we as students use "const value" and not "const reference"? Or would one have the same results for both?

I do hope my questions are clear now
Last edited on
You need to understand the difference between 'pass by value' and 'pass by ref'.

pass by value is the standard. It creates a copy of the passed variable. Changes made within the function are not reflected back in the caller

pass by ref. No copy is made and changes made within the function are reflected back in the caller. Can only be used with a passed variable whose contents can be changed.

Now for each, consider const.

It's not usual to specify const when a variable is passed by value. It's sometimes used as a compile check to make sure its value isn't mistakenly changed in the function when it shouldn't.

However, when used with passed by ref it means that the contents can't be changed and hence the original passed data can't be changed. This means that expressions that can't be changed (eg literals, numbers etc) can be passed by const ref.

Generally, int, double, char (and their variations) are passed by value unless the changes are to be reflected back to the caller then they are passed by ref.

Everything else is generally passed by const ref unless you want the changes reflected back to the caller, in which case it is passed by ref. Only when an explicit copy of what is passed (for non int, double char etc) is it passed by value.

Doing a copy of a variable that is not int/double char is often 'expensive' in terms of performance and hence to be avoided if possible.
Lets take a by value example:
1
2
3
4
5
6
7
8
void func( T xx ) {
  // use xx
}

int main() {
  T yy;
  func( yy );
}

It is very similar to "inlined code":
1
2
3
4
5
6
7
int main() {
  T yy;
  {
    T xx = yy; // copy construction
    // use xx
  }
}

The by value function's code does reserve memory from stack for the parameter and copies value of caller's argument to that parameter.

What you are asking is that if the by value parameter is const, then is it possible for the compiler to perform a copy elision optimization?
Even if it can, the result effect should remain "as if" a copy were made.
Thank you. The answer would be:
1 Where could one see a "copy" if one uses "const value" ?
I do not understand why a copy would be needed if the value in the end will not change because the "const" is there
.
1 Not answered. Question is about the answer of Keskiverto:
Passing parameters by reference does not create a copy, like the by value does.

I am assuming that he refers to a copy inside the function. Regardless, even if one does have a copy inside the function or the real value, it would make no sense because the variable can not be changed. Because it is const.

2 Does he need to write on the above code "const reference" or it is a waste of time that gives no changes and one could simply write "const value" and have the same result?

2 By reading the above answer:
It's not usual to specify const when a variable is passed by value. It's sometimes used as a compile check to make sure its value isn't mistakenly changed in the function when it shouldn't.

Yes. Both would have the same result, although it is not usual to use "const value".

3 Would have the above example any significant change if we as students use "const value" and not "const reference"? Or would one have the same results for both?

3 For the above example same results.

I do hope the answers are correct. If they are, topic can be closed. If not, please correct me.

And thank you all for your help (Y)
Last edited on
> I do not understand why he would need to put const reference if he is not using super big variables

Passing an integer by reference to const (as your professor has done) is usually quite silly.
This is what the core guidelines has to say:
F.16: For “in” parameters, pass cheaply-copied types by value and others by reference to const

Reason Both let the caller know that a function will not modify the argument, and both allow initialization by rvalues.

What is “cheap to copy” depends on the machine architecture, but two or three words (doubles, pointers, references) are usually best passed by value. When copying is cheap, nothing beats the simplicity and safety of copying, and for small objects (up to two or three words) it is also faster than passing by reference because it does not require an extra indirection to access from the function
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-in.


From 'C++ Coding Standards: 101 Rules, Guidelines, and Best Practices' by Alexandrescu and Sutter:
For input-only parameters:
.. Prefer taking inputs of primitive types (e.g., char, float) and value objects that are cheap to copy (e.g., Point, complex<float>) by value.
.. Prefer taking inputs of other user-defined types by reference to const.


The example that your professor gave might have been a somewhat contrived one, merely intended to show the students different ways of passing arguments to functions.


> 1 Where could one see a "copy" if one uses "const value" ?
> I do not understand why a copy would be needed if the value in the end will not change
> because the "const" is there

If the optimiser can see the implementation of the function, it can recognise that a copy would not be needed and optimise away the copy.


> It's not usual to specify const when a variable is passed by value.

It is quite unusual; so unusual that the compiler quietly drops the (top-level) const-qualifier to determine the type of the function. These two declarations declare the same function (they are not two different functions):
1
2
void duplicate( const int a, const int b, const int c ) ;
void duplicate( int a, int b, int c ) ;

Thank you very much JLBorges, now it is perfectly clear :)
On the code we receive from the Professor he sometimes uses as parameter:
const int&
or
const T&

The T in const T& sounds like a templated typename.

If there really is a very generic function template, where T could be either "primitive type" or "user-defined type", then the by reference parameter could make sense.

The use of int as concrete example of T still makes no sense.
Topic archived. No new replies allowed.