不相关的专业化必须存在才能编译?

发布于 2024-11-27 11:53:04 字数 1378 浏览 1 评论 0原文

下面的代码(可以正确编译和执行,做我想要的事情)是我在编写一个类来存储各种类型的属性时遇到的一个奇怪的最小示例,这些类型需要在不再知道其类型时删除指针的能力。我的解决方案是使用模板化函数创建一个删除器类,该函数可以获取并存储其地址以删除特定类型。 我不明白为什么这段代码有效,特别是:

  • 为什么它没有命中断言?
  • 为什么/如何需要/使用(看似)不相关的专业化?

代码:

#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 扩展

输出:

被摧毁

The following code (which compiles and executes properly, doing what I want) is a minimal example of an oddity I experienced while writing a class to store properties of various types that needed the ability to delete pointers when it no longer knows their types. My solution was to make a Deleter class with a templated function that could have its address taken and stored to delete a specific type. I don't understand why this code works, specifically:

  • Why doesn't it hit the assert?
  • Why/how does it require/use the (seemingly) unrelated specialization?

Code:

#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);
}

Compiler: MSVC++ 2010, Microsoft Extensions Disabled

Output:

Destroyed

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

明月松间行 2024-12-04 11:53:04

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 行:编译器不会抱怨 class locale 未声明。

This

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

is not a specialization. This is an overload. A specialization would be like this

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

So actually it's equivalent to writing just

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

Actually, the behavior you presented is because of the overload resolution on the line

T_delete_function = &Deleter::Delete<T>;

The second overload is more specific as it accepts void* and not T* and type is specified explicitly anyway. So in presence of the mentioned overload it chooses it and it compiles and runs finely. In absence of this more specific overload, compiler calls another appropriate, but more general one which fires the assertion.

You can double check that, i.e. remove the #include <locale> line: compiler will not complain about class locale being undeclared.

情深缘浅 2024-12-04 11:53:04

这里没有专门化:您有两个(不同的)重载函数模板:

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

&Deleter::Delete 可以引用任一 Delete 函数模板。在您的示例中选择 (2) 是因为它的类型与您要分配的函数指针的类型匹配。函数指针的类型为void(*)(void*)(1) 转换为函数指针的类型为 void(*)(T*),除非 T = void,否则它会匹配。

There are no specializations here: you have two (different) function templates that overload:

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

&Deleter::Delete<T> could refer to either of the Delete function templates. (2) is selected in your example because its type matches the type of the function pointer to which you are assigning. The type of the function pointer is void(*)(void*); the type of (1) converted to a function pointer is void(*)(T*), which does match unless T = void.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文