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
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.
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
.
精彩评论