Специализация шаблона функции не удалась? - PullRequest
6 голосов
/ 21 января 2012
#include <iostream>
template <class T> 
void foo(T) {
    std::cout << "foo(T)" << std::endl;
}

template <class T> 
void foo(T*) { //#3
    std::cout << "foo(T*)" << std::endl;
}

#define TEST

#ifdef TEST
template <> 
void foo(int*) { //#1
    std::cout << "foo(int*)" << std::endl;
}
#else
template <>
void foo<int*>(int*) { //#2
    std::cout << "foo<int*>(int*)" << std::endl;
}
#endif

int main(int argc, char **argv) {
    int* p = 0;
    foo(p);
    return 0;
}

В чем разница между № 1 и № 2. Если я определю ТЕСТ, № 1 сработает. Но если я это прокомментирую, # 3 работа ... И какой правильный способ написать специализацию шаблона функции ...

Ответы [ 3 ]

3 голосов
/ 21 января 2012

# 1 объявляет специализацию шаблона функции # 3 и автоматически выводит параметры шаблона.# 2 - это специализация первого определенного вами шаблона (без номера, назовем его # 0) для T=int*.Это не может быть специализация # 3, потому что замена T на указанный int* приведет к параметру int**.

Когда вы вызываете foo, разрешение перегрузки теперь сначала выбираетлучше всего подходит базовый шаблон, затем проверяет этот шаблон на наличие существующих специализаций.С определением TEST существует два базовых шаблона (# 0 и # 3), и # 3 является лучшим соответствием и выбирается.Затем компилятор проверяет наличие специализаций этого шаблона, и номер 1 лучше подходит и вызывается.

Без определения TEST есть еще два базовых шаблона (# 0 и # 3) и # 3лучше подходит и выбирается.Затем компилятор проверяет наличие специализаций этого шаблона, но поскольку # 2 специализируется на # 0, а не на # 3, он не учитывается, и # 3 заканчивается вызовом.

Это классический пример Почемуне специализированные шаблоны функций .Проблемы объясняются более подробно там.

Простое решение состоит в том, чтобы вообще не специализировать шаблоны функций, а просто добавить новые перегрузки для специальных типов:

// no template, just a normal function
void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}
2 голосов
/ 21 января 2012

Для специализаций шаблонов функций вы можете явно перечислить аргументы шаблона, но это не обязательно, если выводятся аргументы шаблона.Если вы не укажете аргументы шаблона, они будут выведены компилятором с использованием тех же правил, что и при разрешении перегрузки.Чтобы решить, какую перегрузку функции следует выбрать, компилятор начинает с просмотра только первичных шаблонов (которые выбираются каким-то магическим процессом в первую очередь).Если посмотреть на два доступных основных шаблона

template <typename T> void foo(T);
template <typename T> void foo(T*);

, последний лучше подходит для аргумента указателя.Как только правильный первичный шаблон найден, компилятор ищет потенциальные специализации этого первичного шаблона.Однако ваш пример № 2 на самом деле не является специализацией шаблона функции, принимающего аргумент указателя, хотя он включает аргумент указателя.Если вы берете основное объявление

template <typename T> void foo(T*);

и заменяете T явно указанным аргументом шаблона int*, вы получаете

template <> void foo<int*>(int**);

То есть объявление

template <> void foo<int*>(int*);

это что-то другое.Вы, вероятно, просто хотите потерять указатель при указании аргумента шаблона:

template <> void foo<int>(int*);
0 голосов
/ 21 января 2012

Я не могу точно сказать, какая функция # 2 должна специализироваться, или как именно чрезвычайно сложные правила разрешения перегрузки выбирают функцию для вызова.

Я знаю, что вам чаще всего не требуется для специализированных функций, но вместо этого вы можете полагаться на перегрузку. Чтобы получить функцию для int* вам просто нужно

void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}

Функция, отличная от шаблона, будет предпочтительнее шаблонов, если этот параметр соответствует.

...