编译时必须存在无关的专业化?

时间:2011-07-30 18:24:24

标签: c++ templates pointers delete-operator specialization

下面的代码(正确地编译和执行,做我想做的事)是我在编写一个类来存储各种类型的属性时遇到的奇怪的一个奇怪的例子,当它不再知道它们时需要能够删除指针类型。我的解决方案是使用模板化函数创建一个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已禁用

输出:

  

销毁

2 个答案:

答案 0 :(得分:5)

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未声明。

答案 1 :(得分:3)

这里没有专业化:你有两个(不同的)功能模板重载:

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才匹配。