2017-04-08 5 views
2

私はC++ 11を使用していてアプリケーション内で一般的なHandleクラスを設定しようとしていました。基礎となる型が祖先/子孫として関連している場合は、変換しようとすると失敗するだけです。私はまた、2つのタイプの間で変換が可能であるかどうかを私に知らせる関数も必要です。特に、基底の型がそれ自身の祖先/子孫線の中にない型に変換しようとするのは望ましくないので、私はコンパイル時にコンパイル時に教えられたブーリアンにテンプレート型ファンクタを定義すれば関連していない場合は変換を拒否するためにテンプレートの特殊化を使用し、関連する場合は変換の要求を基になる型に転送します。各基本クラスには、階層内の対応する各タイプに変換する方法を知るテンプレート変換関数と、クラスインスタンスの内部状態に基づいてそのような変換が可能かどうかを示すテンプレートブール関数が含まれています。テンプレートを使用して呼び出す関数をコンパイル時に選択する

template<class T> 
class MyHandle { 
public: 
    ... 
    template<bool> struct can_be_ref {  
     template<class U> bool operator()(const MyHandle *, const U*) const 
     { 
     } 
    }; 

    template<bool> struct as_ref { 
     template<class U> MyHandle<U> operator()(const MyHandle *, const U*) const 
     { 
      throw std::runtime_error("Illegal type conversion"); 
     } 
    }; 
    template<class U> bool can_be(); 
    template<class U> MyHandle<U> as(); 
private: 
    const T* get_member_reference() const; 
}; 

template<class T> struct MyHandle<T>::can_be_ref<true> {  
    template<class U> bool operator()(const MyHandle<T> *ptr, const U*) 
    { 
     ptr->get_member_reference()->can_be<U>(); 
    } 
}; 

template<class T> struct MyHandle<T>::as_ref<true> {  
    template<class U> MyHandle<U> operator()(const MyHandle<T> *ptr, const U*) const 
    { 
     return ptr->get_member_reference()->as<U>(); 
    } 
}; 

template<class T> template<class U> bool MyHandle<T>::can_be() 
{ 
    return can_be_ref < std::is_base_of<T, U>::value || std::is_base_of<U, T>::value >()(this, reinterpret_cast<const U *> (nullptr)); 
} 

template<class T> template<class U> MyHandle<U> MyHandle<T>::as() 
{ 
    return as_ref < std::is_base_of<T, U>::value || std::is_base_of<U, T>::value >()(this, reinterpret_cast<const U *> (nullptr)); 
} 

しかしこれは、コンパイルされない、と私は私が間違っているのかわからない:私は一緒に入れて何

は次のようになります。このエラーは、can_be_refas_ref構造体を特化しようとするところで起こります。コンパイラは、あまりにも少ないテンプレートパラメータリストについて不平を言っています。

私がしたいことは、私が提供した説明と残念なコードの断片の間ではっきりと分かりますが、私がやろうとしていることを記述するために考えることができる唯一の方法です。私は間違って何をしていますか?

EDIT:

明確化、私は次のクラス階層を持っていることを言う:

class A { 
public: 
    template<class U> bool can_be(); 
    template<class U> MyHandle<U> as(); 
... 
}; 

class B : public A{ 
... 
}; 

class C { 
public: 
    template<class U> bool can_be(); 
    template<class U> MyHandle<U> as(); 
... 
}; 

各階層にのみその階層内のアイテムとの懸念自体、および中と定義can_beas方法があります特に、テンプレートへの引数が正しい型でない場合、コンパイルエラーが発生する可能性があります。そのため、コンパイル時に型をチェックする必要があります。
そして、我々は以下の変数が定義されていることを前提としています関連のタイプがある

MyHandle<A> a; 
MyHandle<B> b; 
MyHandle<C> c; 

aのでとbA::can_beA::asは自由にそれらの間で使用することができますが、:: can_beは、コンパイラエラーが生じる可能性があります。したがって、MyHandleでそれらの周りのラッパーはこれを隠すので、たとえばMyHandle<A>::can_be<C>()はfalseを返します。 MyHandle<B>::as<C>()は常に例外をスローしますが、B::as<C>への呼び出しを生成しようとしても、コンパイルエラーが発生する可能性があります。

編集:以下

パーカミルの提案、ソリューションは、周囲のクラスにテンプレート定義をmigateすることでした。私がやったことは、次のようにヘルパーのテンプレートを作成することでした。

template<class T,class U,bool> class MyHandleConverter 
{ 
public: 
    inline MyHandleConverter(const MyHandle<T> *) { } 
    inline bool can_be() const { return false; } 
    inline MyHandle<U> as() const { return MyHandle<U>(nullptr); } 
}; 

私は無効なコンバージョンに例外をスロー見送ることを決めた、と今MyHandleの各インスタンスは、valueと呼ばれるボイドポインタを含む詳細な情報へのポインタを含むことができますそれが無効である場合nullptrあり、実際の基になる型、および次のように私は、その後MyHandleConverterClassのための部分的な特殊化を作成することができます。

template<class T,class U> class MyHandleConverter<T,U,true> { 
public: 
    inline MyHandleConverter(const MyHandle<T> *ref):reference(ref) { }  
    inline bool can_be() const { 
     if (std::is_base_of<T,U>::value) { 
      return true; 
     } else if (reference->value == nullptr) { 
      return false; 
     } else { 
      return reference->underlying_can_be((const U*)(nullptr)); 
     } 
    } 
    inline MyHandle<U> as() const { 
     if (std::is_base_of<U,T>::value) { 
      return MyHandle<U>(reference->value); 
     } else if (reference->value == nullptr) { 
      return MyHandle<U>(nullptr); 
     } else { 
      return reference->underlying_as((const U*)(nullptr)); 
     } 
    } 
private: 
    const MyHandle<T> *reference;  
}; 

の代わりに私が以前に行ったように例外をスローし、私の代わりに、無効なMyHandleを返します(特別なコンストラクタを持つMyHandle(nullptr_t)MyHandleのステータスは、単純なブール値のis_valid()メソッドで照会することができます(呼び出し側が必要に応じて例外をスローしますが、これは目的のためにtry .... catchブロック私がas<U>関数が例外時に例外自体をスローした場合よりも)。

MyHandleクラスはテンプレートunderlying_can_be方法及び鋳型underlying_as方法、単に前方にそれぞれ基礎となるクラス型のcan_be又はas方法への要求を有しています。それはMyHandleConverter<T,U,true>クラスを介して呼び出されていなかった場合は、これらの方法でも、コンパイラによって生成されないことは注目に値しますので、今MyHandle can_beas方法は、このように書かれています:

are_handle_types_relatedはテンプレートconstexprのある
template <class T> template<class U> bool MyHandle<T>::can_be() const { 
    return MyHandleConverter<T, U, are_related_handle_types<U,T>()>(this).can_be(); 
} 

template<class T> template<class U> MyHandle<U> MyHandle<T>::as() const { 
    return MyHandleConverter<T, U, are_handle_types_related<U,T>()>(this).as(); 
} 

MyHandleのcan_beまたはhasメソッドの基になる型を呼び出すと、コンパイラエラーが発生しない場合や、コンパイル時に検出できない論理エラーが発生する可能性がある場合にtrueを返す関数実行時に、複雑な検出ロジックを書かずに、各基本タイプに対応します。つのクラスがそれぞれ適切な型から派生したものであることを単に検出することによって、変換プロセスが妥当に成功することを保証する。 are_handle_types_relatedによって検出されるようなタイプに互換性がない、そして対応するタイプのcan_beas方法、作成されたMyHandleConverterのインスタンスを呼び出すことが無効になり

その方法は、基本となるクラスを呼び出そうとしませんMyHandleConverter<T,U,false>ですMyHandleConverter<T,U,true>が実行されますが、基になる型の適切なconverstion関数を呼び出すことが既に容認されているクラスに対してのみインスタンス化されます。しかし、これはどちらかのコンパイルされません

template<class T> // Template parameter for 'MyHandle<T>' 
template<> // No unspecialized template parameters for 'can_be_ref', but indicate that it is a template anyway 
struct MyHandle<T>::can_be_ref<true> 
{  
    template<class U> bool operator()(const MyHandle<T> *ptr, const U*) 
    { 
     ptr->get_member_reference()->can_be<U>(); 
    } 
}; 

:あなたのような、専門の前にtemplateキーワードを追加する必要がテンプレートを特化する

+0

それはあなたがそれらを使用する方法を私に明確ではありません。有効な用途は何ですか?いくつかの無効な用途は何ですか?それらを掲示することは非常に便利です。 –

答えて

2

http://en.cppreference.com/w/cpp/language/template_specializationによると:

メンバーまたはメンバーのテンプレートは、多くの囲むクラス テンプレート内にネストすることができます。そのようなメンバーの明示的な専門化では、具体的には が特化されているすべての囲むクラステンプレートに対して、 テンプレート<があります。そのようなネストされた宣言では、レベルのいくつかは、 (その外側のクラスが未分化であれば、それはクラスのメンバ テンプレートを特化することができないことを除いて)未分化のままかもしれ

だから我々は完全にせずにテンプレートを特化することはできませんMyHandleにも特化しています。その後、我々は、部分的な特殊化のために行くことができ

template<class T> 
class MyHandle 
{ 
public: 
... 
    template<class U, bool> 
    struct can_be_ref 
    { 
     bool operator()(const MyHandle<T> *ptr, const U*) const 
     { 
      return false; 
     } 
    }; 

    template<class U, bool> 
    struct as_ref 
    { 
     MyHandle<U> operator()(const MyHandle<T> *, const U*) const 
     { 
      throw std::runtime_error("Illegal type conversion"); 
     } 
    }; 
... 
}; 

::私は、例えば、クラスのためにそれを遵守したときに実際に

template<class T> 
template<class U> 
struct MyHandle<T>::can_be_ref<U, true> 
{ 
    bool operator()(const MyHandle<T> * ptr, const U*) const 
    { 
     return ptr->get_member_reference()->can_be<U>(); 
    } 
}; 

template<class T> 
template<class U> 
struct MyHandle<T>::as_ref<U, true> 
{ 
    MyHandle<U> operator()(const MyHandle<T> *ptr, const U*) const 
    { 
     return ptr->get_member_reference()->as<U>(); 
    } 
}; 

template<class T> 
template<class U> bool MyHandle<T>::can_be() const 
{ 
    return can_be_ref<U, 
      std::is_base_of<T, U>::value || std::is_base_of<U, T>::value>()(
      this, nullptr); 
} 

template<class T> 
template<class U> MyHandle<U> MyHandle<T>::as() 
{ 
    return as_ref<U, 
      std::is_base_of<T, U>::value || std::is_base_of<U, T>::value>()(
      this, nullptr); 
} 

Acan_be_ref::operator()からcan_be_refレベルへの移行パラメータU - ソリューションは、テンプレートパラメータの部分的な特殊かもしれBCは、return ptr->get_member_reference()->can_be<U>();expected primary-expression before ')' tokenという行に不満を示しました。私は本当にここで問題が何かを得ることはありません。 get_member_reference()->A::can_be<U>()のように呼んでいます。働いていた問題を回避するには、タイプUの引数を渡すことでcan_be<U>()ためUパラメータを決定することであった:

class A { 
public: 
    template<class U> bool can_be(const U*) 
{ 
return can_be<U>(); 
} 
    template<class U> MyHandle<U> as(const U*) 
{ 
return as<U>(); 
} 

    template<class U> bool can_be(); 
    template<class U> MyHandle<U> as(); 
}; 

template<class T> 
    template<class U> 
    struct MyHandle<T>::can_be_ref<U, true> 
    { 
     bool operator()(const MyHandle<T> * ptr, const U* uptr) const 
     { 
      return ptr->get_member_reference()->can_be(uptr); 
     } 
    }; 

    template<class T> 
    template<class U> 
    struct MyHandle<T>::as_ref<U, true> 
    { 
     MyHandle<U> operator()(const MyHandle<T> *ptr, const U* uptr) const 
     { 
      return ptr->get_member_reference()->as(uptr); 
     } 
    }; 
関連する問題