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 const
s 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?
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)
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.”
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