私は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_ref
とas_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_be
とas
方法があります特に、テンプレートへの引数が正しい型でない場合、コンパイルエラーが発生する可能性があります。そのため、コンパイル時に型をチェックする必要があります。
そして、我々は以下の変数が定義されていることを前提としています関連のタイプがある
MyHandle<A> a;
MyHandle<B> b;
MyHandle<C> c;
a
のでとb
、A::can_be
とA::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_be
とas
方法は、このように書かれています:
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_be
やas
方法、作成された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
キーワードを追加する必要がテンプレートを特化する
それはあなたがそれらを使用する方法を私に明確ではありません。有効な用途は何ですか?いくつかの無効な用途は何ですか?それらを掲示することは非常に便利です。 –