2011-07-30 22 views
4

下面的代碼(編譯和正確執行,做我想做的事)是我在編寫類時存儲的一個奇怪例子,它存儲了各種類型的屬性,這些屬性需要在不再知道它們的類型時刪除指針的能力。我的解決方案是使用模板化函數創建一個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,微軟擴展殘疾人

輸出:

銷燬

+0

我假定你也是爲了'#include ' –

+0

@Travis - 它沒有它就編譯好,但爲了清晰起見我添加了它。 – 0x5f3759df

回答

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

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確實匹配。

+0

所以這段代碼只能工作,因爲它將void *變爲locale *並且析構函數在每個類的內存中都處於相同位置? – 0x5f3759df

+0

不,在你的第二個函數模板中,'locale'不會命名一個類型;它將該類型參數命名爲函數。你可以用'T'或'MyAwesomeType'替換它,它將完全相同。 –

+0

@詹姆斯,哦,那是在我自己的回答中給我造成困擾的事情,現在編輯,演員也是'T *',忘了那個:) thx – unkulunkulu

相關問題