Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

function template overloading: const* vs. const&

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"
like image 856
idefixs Avatar asked May 15 '13 14:05

idefixs


3 Answers

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.

like image 133
Mark B Avatar answered Oct 21 '22 11:10

Mark B


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

like image 3
ChrisCM Avatar answered Oct 21 '22 12:10

ChrisCM


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

like image 1
Michael Anderson Avatar answered Oct 21 '22 13:10

Michael Anderson