5

Consider the following code snippets:

void foo(const int i) // First foo
{
   std::cout << "First " << i << endl;
}

void foo(int i)       // Second foo
{
   std::cout << "Second " << i << endl;
}

int main() 
{
   int i = 5;
   foo(i);      
}

Compilation Error: redefinition of 'void foo(int)'

Since consts can be initialized with non-const objects, the above behaviour seems reasonable. Now consider this:

void foo_ptr(const int* p)  // First foo_ptr
{
   std::cout << "First " << *p << endl;
}

void foo_ptr(int* p)        // Second foo_ptr
{
   std::cout << "Second " << *p << endl;
}

int main()
{
   int i = 5;
   foo_ptr(&i);             // Second foo_ptr gets called; prints 'Second 5'
}

As it might be clear, my question is - If the two definitions of foo in the first case are considered the same then why it is not so for foo_ptr in the second case? Or in other words, why const is ignored in the first case and not so in the second one?

stillanoob
  • 1,279
  • 2
  • 16
  • 29
  • possible duplicate http://stackoverflow.com/questions/3682049/functions-with-const-arguments-and-overloading – ThomasMcLeod Jun 20 '16 at 03:40
  • That dupe explains why const int and int signatures are considered identical but the OP understands this quite well (as per the first example). It does not cover why the pointer example does not have the same issue. – paxdiablo Jun 20 '16 at 06:34

4 Answers4

5
const int* p

is not a constant pointer to an integer, it's a pointer to a constant integer (i.e., [const int] * p rather than const [int * p]). This is why you sometimes see code like:

const int * const p;

which may seem redundant to the uninitiated but is really not - p in that case is a pointer you're not allowed to change, which points to an integer you're also not allowed to change.

Hence the two functions you have in your second case are considered different in terms of the parameters accepted. That's also why you're calling the second function, since i is most definitely not a const integer.

In other words, while const-ing a parameter does not change it in terms of the function signature, that's not what you're doing here. Changing a parameter from "pointer to int" to "pointer to const int" does affect the signature.

The equivalent case to your first code snippet would be providing both of:

void foo_ptr (int * const p)
void foo_ptr (int * p)
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
2

During overload resolution, const and volatile specifies on parameters are significant except when they occur at the outermost level of the of the parameter type specification. From the C++ standard, § 13.1.3.4:

Parameter declarations that differ only in the presence or absence of const and/or volatile are equivalent. That is, the const and volatile type-specifiers for each parameter type are ignored when determining which function is being declared, defined, or called. [ Example:

typedef const int cInt;
int f (int);
int f (const int); // redeclaration of f(int)
int f (int) { /* ... */ } // definition of f(int)
int f (cInt) { /* ... */ } // error: redefinition of f(int)

—end example ] 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. 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.”

ThomasMcLeod
  • 7,603
  • 4
  • 42
  • 80
0

why const is ignored in the first case and not so in the second one?

In the 1st case, const is qualified for the parameter itself, while in the 2nd case, const is qualified for the pointee, not the pointer itself. Const pointer and pointer to const are not the same thing.

In the 2nd case, pointer to const and pointer to non-const are different and acceptable for overloading. If you make the pointer itself const, i.e. int* const p vs int* p, you'll get the same result as the 1st case.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
0

Because when you declare

void foo(const int n)
{
}

All that the const modifier does is prevent n from being modified inside the foo() function. The parameter to this foo() function is still an int. The const modifier does not modify the parameter's type. So both

void foo(int n)

and

void foo(const int n)

are functions that take an int parameter. The only difference between them is that the second one cannot modify it's parameter, while the first one can modify it, like any other non-const variable inside the function.

However, there is a difference between

void foo(const int *p)

and

void foo(int *p)

One is a pointer to a const integer, the other one is a pointer to a mutable integer. They are different types.

Bonus answer:

Both

void foo(int *p)

and

void foo(int * const p)

have the same parameter type. Both functions' parameter is a pointer to an int. Except that the second one's parameter is const value, and the function cannot modify it.

Confused yet?

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • So the function definitions differ when the parameters are of different type rather than when the functions perceive them to be of different types. – stillanoob Jun 20 '16 at 01:56
  • More, or less true. `const`/`volatile` qualifiers when applied to the "top" portion of the type do not actually affect the parameter type. – Sam Varshavchik Jun 20 '16 at 01:59