Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 298
user997112 Avatar asked Jun 10 '13 21:06

user997112


People also ask

Can const and volatile be used together?

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

Can constants be passed by reference?

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.

Can const function be overloaded?

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.

Why do we use const in operator overloading?

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.


2 Answers

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.

like image 56
juanchopanza Avatar answered Oct 21 '22 11:10

juanchopanza


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

like image 28
statueuphemism Avatar answered Oct 21 '22 11:10

statueuphemism