Search code examples
c++templatesargument-dependent-lookup

What is the preference of function/method/template name resolving in C++?


How does the C++ compiler decide which function/method to call if there are multiple possibilities? In my specific case I have the standard free function of the C++ Run time and I also have a templated free variant, like this:

// The definitions of the C++ Run Time Library (from memory.h)
extern malloc(size_t s);
extern void free(void *p);

// Our own memory management functions
extern void *OurMalloc(size_t s);
extern void OurFree(void *p);

// Own variants to overrule malloc and free (instead of using #define)
template<typename T>
void *malloc(T t)
{
return OurMalloc(t);
}

template<typename T>
void free(T *t)
{
OurFree(t);
}

I tested this using the following code:

void main(void)
{
void *p = malloc(10);
free(p);
}

If I compile and run this, it seems that the call to malloc is correctly replaced by the templated variant. So far, so good.

However, the call to free is not replaced by the templated variant, and the standard C++ function is still called.

What rules does the C++ compiler use to decide which variant to give priority? Is this related to the Koenig-lookup rules?

Note: I tried this alternative because using #define does not solve the problem (see question How to use C macro's (#define) to alter calls but not prototypes).


Solution

  • Overload resolution is quite complicated in general.

    In your case, it is quite easy: a function template is not considered if there is an exact match. For free it is the case (the standard free takes a void*), for malloc it isn't (the standard malloc takes a size_t, you are passing an int and size_t can't be a typedef for int -- size_t is unsigned). If you call free with a type other than void*, it should instantiate your template.

    Running:

    #include <iostream>
    
    void* ml(size_t s)
    {
        std::cout << "ml(size_t)\n";
    }
    
    void fr(void *p)
    {
        std::cout << "fr(void*)\n";
    }
    
    template<typename T>
    void* ml(T t)
    {
        std::cout << "ml<" << typeid(T).name() << ">(T)\n";
    }
    
    template<typename T>
    void fr(T *t)
    {
        std::cout << "fr<" << typeid(T).name() << ">(T*)\n";
    }
    
    int main()
    {
        void* p1 = ml((size_t)10);
        fr(p1);
        int* p2 = (int*)ml(10);
        fr(p2);
        return 0;
    }
    

    I get

    ml(size_t)
    fr(void*)
    ml<i>(T)
    fr<i>(T*)
    

    and i is what returns typeid(int).name()