2011-07-30 6 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は、Microsoft拡張機能障害者

出力:

破壊

+0

あなたも '#include ' –

+0

@Travisとすることを前提としています。それは問題なくコンパイルできますが、分かりやすくするために追加しました。 – 0x5f3759df

答えて

5

この

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

が専門ではありません。これは過負荷です。専門は実際に、あなたが提示現象は、ライン上のオーバーロードの解決

T_delete_function = &Deleter::Delete<T>; 

であり、この

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

ようなので、実際にはそれだけで

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

を書くことと等価だだろう第2のオーバーロードは、void*を受け入れ、T*ではなく、明示的に指定されているので、より具体的です。したがって、前述のオーバーロードの場合には、が選択され、コンパイルされ、細かく実行されます。このより具体的なオーバーロードがない場合、コンパイラはアサーションを起動する別の適切な、しかしより一般的なものを呼び出します。

#include <locale>行を削除してください:コンパイラはclass localeが宣言されていないと不平を言うことはありません。

3

何の専門分野はここにありません:あなたは持っています2つ(異なる)オーバーロード関数テンプレート:

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 *をロケール*にキャストしているだけで、デストラクタは各クラスのメモリ内の同じ場所にあるので動作しますか? – 0x5f3759df

+0

いいえ、2番目の関数テンプレートで 'locale'は型を指定しません。関数に型パラメーターを指定します。あなたは 'T'または' MyAwesomeType'と置き換えることができ、まったく同じになります。 –

+0

@ジェームス、ああ、それは私自身の答えで私を盗んだものです、今キャストは 'T *'にもあります、それについて忘れました:) thx – unkulunkulu

関連する問題