I have the code:
#include "stdafx.h"
#include <iostream>
using namespace std;
void func(const int& a)
{
std::cout << "func(const)" << std::endl;
}
void func(volatile int& a)
{
std::cout << "func(volatile)" << std::endl;
}
void func(const volatile int& a)
{
std::cout << "func(const volatile)" << std::endl;
}
int main()
{
const int a = 0;
const volatile int b = 0;
volatile int c = 0;
func(a);
func(b);
func(c);
system("pause");
return 0;
}
The above code shows overloading based on whether the parameters are const/volatile. However, if I were to change the parameters from int&
to int
, the code no longer compiles and I cannot overload based upon const/volatile parameter types. I dont get why we can overload based on const and volatile if the int is passed by reference, but not if its passed by value?
EDIT I should emphasise I understand what a reference does- I do not understand why a reference alias is allowed to overload on const but a normal int is not.
Yes a C++ variable be both const and volatile. It is used in situations like a read-only hardware register, or an output of another thread. Volatile means it may be changed by something external to the current thread and Const means that you do not write to it (in that program that is using the const declaration).
You only pass by reference to non-const if you want to change the arguments and have the client observe those changes. If you don't want to change the arguments, pass by reference to const or by value. If you want to change the arguments but have no effect on the client, pass by value.
Overloading on the basis of const type can be useful when a function returns a reference or pointer. We can make one function const, that returns a const reference or const pointer, and another non-const function, that returns a non-const reference or pointer. See this for more details.
The const qualifier ensures that the parameter (param) is not altered inside the operator=() method. The above method alters the right hand side operand 'param' after assigning the value to the left hand side operand. The const qualifier helps you not to violate the semantics of the assignment operation.
The issue is that the top level const
and/or volatile
are ignored in overload resolution. So
void foo(const int);
is exactly the same as
void foo(int);
and similarly for volatile
. This is a language rule, and it makes sense since the arguments are passed by value. On the other hand, reference to const/volatile
or pointer to const/volatile
have a different meaning: you are not allowed to call non-const/volatile methods on what they refer to or point to. Here, the const
volatile
are not top level.
void foo(int& i); // can modify what i refers to, and this has effects outside of foo.
void foo(const int& i); // cannot modify what i refers to
The two above declarations have very different semantics, so the language makes them distinct concerning overload resolution.
Perhaps it is useful to take a step back from the functions and just look at the use-cases themselves.
First, we will define an integer and a constant integer for use in our examples:
int anInt = 1;
const int aConstInt = 1;
Next, we take a look at what happens when using these variables to set the values of other integers and constant integers:
int a = anInt; // This works, we can set an int's value
// using an int
int b = aConstInt; // This works, we can set an int's value
// using a const int
const int c = anInt; // This works, we can set a const int's value
// using an int
const int d = aConstInt; // This works, we can set a const int's value
// using a const int
As you can see, there is no way to resolve which overload of a function to select based on behavior (a const int can be accepted by both an int and a const int, and likewise an int can be accepted by both an int and a const int).
Next, we shall take a look at what happens when pass the first set of variables to references:
int& a = anInt; // This works because we are using a
// non-constant reference to access a
// non-constant variable.
int& b = aConstInt; // This will NOT work because we are
// trying to access a constant
// variable through a non-constant
// reference (i.e. we could
// potentially change a constant
// variable through the non-const
// reference).
const int& c = anInt; // This works because we are using a
// constant reference (i.e. "I cannot
// try to change the referenced
// variable using this reference") to
// a non-constant variable.
const int& d = aConstInt; // This will work because we are trying
// to access a constant variable
// through a constant reference.
As you can see, there is some useful behavior that can be had out of distinguishing between an int reference and a const int reference (i.e. disallowing creation of a non-constant reference when a constant reference type is expected).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With