Search code examples
c++constantsoverloadingvolatile

Overloading on const and volatile- why does it work by reference?


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.


Solution

  • 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).