开发者

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 开发者_JAVA百科alternative because using #define does not solve the problem (see question How to use C macro's (#define) to alter calls but not prototypes).


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()


For your particular issue about malloc and free, the problem is that in your call to malloc:

void *p = malloc(10);

the parameter 10 is typed as an int, while the signature for the runtime's malloc() calls for an unsigned argument. Since there's not an exact match, the compiler prefers the templated malloc where it can create an exact match.

When you call:

free(p);

the type of p is void* which does exactly match the runtime's signature for free() so the compiler doesn't bother using the templated free.


It is not possible to "replace" the standard malloc using this technique. Other answers have already explained that because you are using a signed value as an argument in malloc call, your templated version happens to "win" over the standard one because the standard one expects an unsigned argument.

To better illustrate this I just wanted to add that if you supply either an unsigned int or unsigned long argument in your malloc call

void *p1 = malloc(10u);
void *p2 = malloc(10ul);

and you'll notice that in one of these calls your templated version of malloc also doesn't "work" anymore and the standard one is called instead, since it is a better match for the argument (provided that on your platform size_t is defined as either unsigned int or unsigned long)


Not answering the question you asked, but what it seems like you're trying to do:

If it's available on your system, you can use LD_PRELOAD to preload a .so library you build that has your versions of malloc and free. Then they will definitely be called instead of the standard versions.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜