When I have two templated function overloads like this:
template<class T>
void foo( T const& )
{
    // do something with a const reference
    cout << "const reference version";
}
template<class T>
void foo( T const* )
{
    // do something with a const pointer
    cout << "const pointer version";
}
Why does the compiler choose the first version when instantiated with a non-const pointer type?
int* bar;
foo( bar ); // prints "const reference version"
char const* baz;
foo( baz ); // prints "const pointer version"
                The reason is because bar is a non-const pointer, so int* const& is actually a better match than int const* because it doesn't have to add const to the pointer type.
If bar were const qualified then it would be an exact match for the T const* version.
#include <iostream>
using namespace std;
void foo(int const&) {
    cout << "const reference" << endl;
}
void foo(int const*) {
    cout << "const pointer" << endl;
}
int main() {
    int *hi;
    foo (hi);   //const pointer
    foo((*hi)); //const reference
    foo(&(*hi));//const pointer
}
The deal here is references and pointers are different. A pointer is a unique type, where as a reference to a value is no different from the value itself, or rather, an alias to the object. So for example, this version of the code above will not compile.
#include <iostream>
using namespace std;
void foo(int const&) {
    cout << "const reference" << endl;
}
void foo(int) {
    cout << "hi there" << endl;
}
int main() {
    int hi;
    foo(hi); //const reference
}
As the declarations of foo are ambiguous. The compiler cannot decide between them.
You can determine what is going on with your template types using typeid.
#include <iostream>
#include <typeinfo>
using namespace std;
template<class T> void foo( T const& ) {
    cout << "const reference version T="<< typeid(T).name()<<endl;
}
template<class T> void foo( T const* ) {
    cout << "const pointer version T="<<typeid(T).name()<<endl;
}
int main() {
    int* i_ptr=0;
    foo(i_ptr);
    const int* ci_ptr=0;
    foo(ci_ptr);    
}
This outputs (note exact output will depend on your compiler)
const reference version T=Pi
const pointer version T=i
Which shows that in the first case T = int* and the full argument type is int* const&, and in the second T=int and the full argument type is int const *.
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