Ответ 1
Здесь одна причина в пользу версии шаблона: processPointer<void>(void*)
может быть вызвана напрямую, избегая другая перегрузка.
Предположим, у меня есть шаблон, который работает с необработанными указателями:
template<typename T>
void processPointer(T* ptr);
Я не хочу, чтобы это вызывалось с помощью указателей void*
. Кажется, у меня есть два выбора. Я могу удалить перегрузку без шаблонов:
void processPointer(void*) = delete;
Или я могу удалить экземпляр шаблона:
template<>
void processPointer<void>(void*) = delete;
Объявление перегрузки без шаблонов проще (без фьюзинга с угловыми скобками). Есть ли причины, по которым я предпочел бы удалить экземпляр шаблона вместо этого?
Здесь одна причина в пользу версии шаблона: processPointer<void>(void*)
может быть вызвана напрямую, избегая другая перегрузка.
Я не вижу причин, по которым здесь идет шаблон.
Фактически, удалив нестратегированную перегрузку, вы можете отклонить свой путь от некоторых неоднозначных вызовов крайнего случая, о которых я не могу сейчас думать, поскольку не-шаблоны имеют приоритет над экземплярами шаблонов. И, таким образом, сделать эту работу по желанию в большинстве случаев.
Это может дать представление:
#include <iostream>
struct X
{
template<typename T>
void processPointer(T* ptr) {
std::cout << "Template\n";
}
// error: explicit specialization in non-namespace scope ‘struct X’
// template<>
// void processPointer(void*) = delete;
// Overload but no specialization
// This will prevent lookup the specialization outside the class, when no
// template argument is explicitly given. However, with an explicit
// template argument the specialization is called.
void processPointer(void*) = delete;
};
// Specialization outside the class body
template<>
void X::processPointer(void* ptr) {
std::cout << "Specialization\n";
}
int main ()
{
X x;
//error: use of deleted function ‘void X::processPointer(void*)’
//x.processPointer((void*)0);
// Explicit template argument:
x.processPointer<void>((void*)0);
}
Заключение: Ответ @Casey выполняется.
Предположим, вы хотите передать аргумент pointer
типа void*
(или просто nullptr
) в вашу функцию processPointer
, и вы также хотите назвать его специализацию для типа Type
. Затем вы должны написать
processPointer(static_cast<Type>(pointer));
для
void processPointer(void*) = delete;
Но для
template<>
void processPointer<void>(void*) = delete;
вы можете написать код, который намного короче:
processPointer<Type>(pointer);
Таким образом, оба варианта могут использоваться в разных случаях.
Однако аналог варианта с нештатной перегрузкой может быть единственным способом в некоторых случаях. Предположим, что существует шаблон функции с двумя параметрами:
template<typename T, typename U>
void processPointer(T* ptr1, U* ptr2);
Вы не хотите, чтобы его вызывали с указателями void*
в качестве первого аргумента. Частичная специализация шаблонов функций не допускается в С++, поэтому этот код неверен:
template<typename U>
void processPointer<void, U>(void*, U*) = delete;
И вы должны использовать другой:
template<typename U>
void processPointer(void*, U*) = delete;