Каково предпочтение разрешения функции / метода / шаблона в C ++? - PullRequest
2 голосов
/ 23 февраля 2010

Как компилятор C ++ решает, какую функцию / метод вызывать, если существует несколько возможностей? В моем конкретном случае у меня есть стандартная бесплатная функция времени выполнения C ++, и у меня также есть шаблонный бесплатный вариант, такой как:

// 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);
}

Я проверил это, используя следующий код:

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

Если я скомпилирую и выполню это, похоже, что вызов malloc правильно заменен шаблонным вариантом. Пока все хорошо.

Однако вызов free не заменяется шаблонным вариантом, и стандартная функция C ++ по-прежнему вызывается.

Какие правила использует компилятор C ++, чтобы решить, какой вариант отдать приоритет? Это связано с правилами поиска Кенига?

Примечание: я попробовал этот вариант, потому что использование #define не решает проблему (см. Вопрос Как использовать макрос C (#define) для изменения вызовов, но не прототипов ).

Ответы [ 4 ]

8 голосов
/ 23 февраля 2010

Разрешение перегрузки в общем довольно сложное.

В вашем случае это довольно просто: шаблон функции не учитывается, если есть точное совпадение. Бесплатно это так (стандартное освобождение занимает пустоту *), для malloc это не так (стандартное malloc принимает size_t, вы передаете int, а size_t не может быть typedef для int - size_t не подписано ). Если вы звоните бесплатно с типом, отличным от void *, он должен создать экземпляр вашего шаблона.

Продолжительность:

#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;
}

Я получаю

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

и я это то, что возвращает typeid(int).name()

3 голосов
/ 23 февраля 2010

Для вашего конкретного вопроса о malloc и free проблема заключается в том, что при вашем звонке на malloc:

void *p = malloc(10);

параметр 10 набирается как int, тогда как подпись для malloc() среды выполнения вызывает аргумент unsigned. Поскольку нет точного соответствия, компилятор предпочитает шаблон malloc, где он может создать точное совпадение.

При звонке:

free(p);

тип p равен void*, который в точности соответствует сигнатуре среды выполнения для free(), поэтому компилятор не будет использовать шаблонный free.

2 голосов
/ 23 февраля 2010

Невозможно "заменить" стандарт malloc, используя эту технику. Другие ответы уже объясняли, что, поскольку вы используете значение со знаком в качестве аргумента в вызове malloc, ваша шаблонная версия «выигрывает» над стандартной, поскольку стандартная версия ожидает неподписанный аргумент.

Чтобы лучше проиллюстрировать это, я просто хотел добавить, что если вы указали аргумент unsigned int или unsigned long в вашем malloc вызове

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

и вы заметите, что при одном из этих вызовов ваша шаблонная версия malloc также больше не "работает", и вместо нее вызывается стандартная версия, поскольку она лучше соответствует аргументу (при условии, что на ваша платформа size_t определяется как unsigned int или unsigned long)

0 голосов
/ 23 февраля 2010

Не отвечая на вопрос, который вы задали, но похоже, что вы пытаетесь сделать:

Если он доступен в вашей системе, вы можете использовать LD_PRELOAD для предварительной загрузки созданной вами библиотеки .so, в которой есть ваши версии malloc и бесплатно. Тогда они точно будут называться вместо стандартных версий.

...