Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unrelated specialization must exist to compile?

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

like image 449
0x5f3759df Avatar asked Feb 24 '23 10:02

0x5f3759df


2 Answers

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.

like image 145
unkulunkulu Avatar answered Mar 01 '23 22:03

unkulunkulu


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.

like image 37
James McNellis Avatar answered Mar 01 '23 23:03

James McNellis