Подтвердить что ты не робот

Когда удаляется экземпляр шаблона, предпочтительнее удаления не-шаблонной перегрузки?

Предположим, у меня есть шаблон, который работает с необработанными указателями:

template<typename T>
void processPointer(T* ptr);

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

void processPointer(void*) = delete;

Или я могу удалить экземпляр шаблона:

template<>
void processPointer<void>(void*) = delete;

Объявление перегрузки без шаблонов проще (без фьюзинга с угловыми скобками). Есть ли причины, по которым я предпочел бы удалить экземпляр шаблона вместо этого?

4b9b3361

Ответ 1

Здесь одна причина в пользу версии шаблона: processPointer<void>(void*) может быть вызвана напрямую, избегая другая перегрузка.

Ответ 2

Я не вижу причин, по которым здесь идет шаблон.

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

Ответ 3

Это может дать представление:

#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 выполняется.

Ответ 4

Предположим, вы хотите передать аргумент 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;