Несвязанная специализация должна существовать для компиляции? - PullRequest
4 голосов
/ 30 июля 2011

Следующий код (который компилируется и выполняется правильно, делая то, что я хочу) является минимальным примером странности, с которой я столкнулся при написании класса для хранения свойств различных типов, которые нуждались в способности удалять указатели, когда он больше не знает ихтипы.Мое решение состояло в том, чтобы создать класс Deleter с шаблонной функцией, чей адрес мог бы быть взят и сохранен для удаления определенного типа. Я не понимаю, почему этот код работает , а именно:

  • Почему он не попадает в утверждение?
  • Почему / как он требует / использует(на первый взгляд) несвязанная специализация?

Код:

#include <iostream>
#include <string>
#include <cassert>

#include <locale> //Just here as an unused class to specialize

using namespace std;

typedef void(*void_voidptr_func_t)(void*);

class ClassWithDestructor {
public:
    ~ClassWithDestructor() {
        cout << "Destroyed\n";
    }
};

class Deleter {
public:
    template <class T>
    static void Delete (T* ptr) {
        assert(0);
    }

    //locale here can be any class
    //it doesn't matter what class it is
    //but if this specialization doesn't exist
    //compile fails
    template <class locale>
    static void Delete(void* ptr) {
        delete (locale*)ptr;
    }
};

void* void_ptr_to_T = NULL;
void_voidptr_func_t T_delete_function = NULL;

template<class T>
void A() {
    T* t = new T;
    void_ptr_to_T = (void*)t;
    T_delete_function = &Deleter::Delete<T>;
}

int main(int argc, char** argv) {
    A<ClassWithDestructor>();
    T_delete_function(void_ptr_to_T);
}

Компилятор: MSVC ++ 2010, Microsoft Extensions Disabled

Вывод:

Уничтожено

Ответы [ 2 ]

5 голосов
/ 30 июля 2011

Это

template <class locale>
static void Delete(void* ptr) {
    delete (locale*)ptr;
}

не является специализацией.Это перегрузка.Специализация была бы такой:

template <>
static void Delete(locale* ptr) {
    delete (locale*)ptr;
}

Так что на самом деле это эквивалентно записи просто

template <class T>
static void Delete(void* ptr) {
    delete (T*)ptr;
}

На самом деле, вы представили поведение из-за разрешения перегрузки в строке

T_delete_function = &Deleter::Delete<T>;

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

Вы можете проверить это дважды, то есть удалить строку #include <locale>: компилятор не будет жаловаться на class localeбудучи необъявленным.

3 голосов
/ 30 июля 2011

Здесь нет специализаций: у вас есть два (разных) шаблона функций, которые перегружаются:

template <typename T> void Delete(T*);    // (1)
template <typename T> void Delete(void*); // (2)

&Deleter::Delete<T> может относиться к любому из шаблонов функций Delete. (2) выбран в вашем примере, потому что его тип соответствует типу указателя функции, которому вы назначаете. Тип указателя на функцию void(*)(void*); тип (1) , преобразованный в указатель функции, равен void(*)(T*), что соответствует, если только T = void.

...