2016-05-09 5 views
5

を条件:get()コールでコンパイル時間の派遣:次のコードを考えると、有効なコール

template<typename GroupA, typename GroupB> 
class JoinedObjectGroup 
    : public _ObjectSpaceHolder<GroupA> 
    , public _ObjectSpaceHolder<GroupB> 
    { 
    public: 
     JoinedObjectGroup(GroupA &groupA, GroupB &groupB) 
     : _ObjectSpaceHolder<GroupA>(groupA) 
     , _ObjectSpaceHolder<GroupB>(groupB) 
     { 
     } 

     template<typename ObjectType> 
     ObjectType get() 
     { 
      // Dispatch to appropriate handler: only one of the following actually compiles as 
      // either GroupA knows about ObjectType or GroupB, but not both. So: 
      // 
     // return static_cast<_ObjectSpaceHolder<GroupA> &>(*this).m_objectSpace.get<ObjectType>(); 
      // or 
      // return static_cast<_ObjectSpaceHolder<GroupB> &>(*this).m_objectSpace.get<ObjectType>(); 
     } 
    }; 

は、私は適切なハンドラにコンパイル時のディスパッチを実行したいと思います。その根底にある考え方は、ObjectTypeGroupAまたはGroupBのいずれかであることです。

template<typename ObjectType> 
ObjectType get() 
    { 
    return Dispatch<ObjectType, GroupA, GroupB>::get(*this); 
    } 

で:私の最初のアプローチは、次のことをした

template<typename ObjectType, typename GroupA, typename GroupB, typename = void> 
struct Dispatch; 

template<typename ObjectType, typename GroupA, typename GroupB> 
struct Dispatch<ObjectType, GroupA, GroupB, typename std::enable_if<std::is_same<ObjectType, decltype(std::declval<GroupA>().template get<ObjectType>())>::value>::type> 
    { 
    template<typename JoinedGroup> 
    static 
    ObjectType get(JoinedGroup &joinedGroup) 
     { 
     return static_cast<_ObjectSpaceHolder<GroupA> &>(joinedGroup).m_objectSpace.get<ObjectType>(); 
     } 
    }; 

template<typename ObjectType, typename GroupA, typename GroupB> 
struct Dispatch<ObjectType, GroupA, GroupB, typename std::enable_if<std::is_same<ObjectType, decltype(std::declval<GroupB>().template get<ObjectType>())>::value>::type> 
    { 
    template<typename JoinedGroup> 
    static 
     ObjectType get(JoinedGroup &joinedGroup) 
     { 
     return static_cast<_ObjectSpaceHolder<GroupB> &>(joinedGroup).m_objectSpace.get<ObjectType>(); 
     } 
    }; 

私は、これはenable_ifis_same句でObjectTypeを代入すると、式のいずれかが失敗につながると考え、したがって、残して働くだろうと想定していました1つの有効な専門化のみ。しかし、あいまいな名前や再定義エラーは私を間違っています。

なぜ私の推論は間違っていますか?代わりに、代わりに適切にコールをディスパッチできますか?

+0

@JoachimPileborg:ありがとう、それは名前を単純化するときに導入されたタイプミスでした。これを修正しました。 – OnMyLittleDuck

+2

また、 '_ObjectSpaceHolder'はコンパイラ用に予約されています(アンダースコア - 大文字で始まるすべての名前とともに)。 –

+0

私は上記の仮定は、 'テンプレート T GroupA :: get ()'と 'テンプレート T GroupB :: get ()'のうちの1つだけが存在するということです。 – Smeeheey

答えて

2
namespace details { 
    template<template<class...>class Z, class always_void, class...Ts> 
    struct can_apply : std::false_type {}; 
    template<template<class...>class Z, class...Ts> 
    struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type {}; 
} 
template<template<class...>class Z, class...Ts> 
using can_apply = details::can_apply<Z, void, Ts...>; 

のようなものは、このテンプレートと引数のリストを取り、そしてあなたがそれらを適用することができるかどうかを示します。

foo.get<Bar>()を評価したテンプレート:

template<class ObjectType, class Source> 
using get_template_result = decltype(std::declval<Source>().get<ObjectType>()); 

は、我々が有効に上記のテンプレートを呼び出すことができますか?

template<class ObjectType, class Source> 
using can_get_template = can_apply< get_template_result, ObjectType, Source >; 

私たちはそれを評価することができますタイプにテンプレートを置くためのパッケージ、:

template<template<class...>class Z> 
struct z_template { 
    template<class...Ts> 
    using result = Z<Ts...>; 
}; 

その引数と戻りますが、常に結果破棄同様のパッケージ、:

template<class Result> 
struct z_identity { 
    template<class...>using result=Result; 
}; 

可能であればget_template_resultと評価します。その場合は、そのタイプをObjectTypeと比較します。それ以外の場合は、(偽の保証)ObjectTypeObjectType*を比較:

template<class ObjectType, class Source> 
using get_template_gets_type = std::is_same<ObjectType, 
    typename // maybe? 
    std::conditional_t< 
    can_get_template<ObjectType,Source>, 
    z_template<get_template_result>, 
    z_identity<ObjectType*> 
    >::template result<ObjectType, Source> 
>; 

我々はすべてのことをしたら、我々は派遣にタグを付けることができます!それはあなたが上.get<ObjectType>()を呼び出し、ObjectTypeを返すことができますタイプを見つけるまで今

template<class ObjectType, class T0, class...Ts, class Source> 
ObjectType get_smart(Source&& source, std::true_type) { 
    return static_cast<T0&&>(std::forward<Source>(source)).get<ObjectType>(); 
} 
template<class ObjectType, class T0, class T1, class...Ts, class Source> 
ObjectType get_smart(Source&& source, std::false_type) { 
    return get_smart<ObjectType, T1, Ts...>(std::forward<Source>(source), get_template_gets_type<ObjectType, T1>{}); 
} 
template<class ObjectType, class T0, class...Ts, class Source> 
ObjectType get_smart(Source&& source) { 
    return get_smart(std::forward<Source>(source), get_template_gets_type<ObjectType, T0>{}); 
} 

get_smart<ObjectType, TypeA, TypeB>(something)はその後TypeBをリストTypeAを検索します。その後、停止します。

このようなタイプが見つからない場合、コンパイルは失敗します。

TypeA TypeBとObjectTypeのタイプのリストのr/l値を設定する責任はお客様にあります。リストの長さは、テンプレートの再帰制限(通常は100秒)によって制限されます。

+0

どのようにすっきり!ありがとう、これで多くのことを学んだ! – OnMyLittleDuck

0

template<typename ObjectType, typename GroupA, typename GroupB> 
struct Dispatch; 

template<typename GroupA, typename GroupB> 
struct Dispatch<GroupA, GroupA, GroupB> 
    { 
    template<typename JoinedGroup> 
    static 
    GroupA get(JoinedGroup &joinedGroup) 
     { 
     return static_cast<_ObjectSpaceHolder<GroupA> &>(joinedGroup).m_objectSpace.template get<GroupA>(); 
     } 
    }; 

template<typename GroupA, typename GroupB> 
struct Dispatch<GroupB, GroupA, GroupB> 
    { 
    template<typename JoinedGroup> 
    static 
     GroupB get(JoinedGroup &joinedGroup) 
     { 
     return static_cast<_ObjectSpaceHolder<GroupB> &>(joinedGroup).m_objectSpace.template get<GroupB>(); 
     } 
    }; 

については何?

あなたの前提は私にとっては難しそうです。あなたのコードをコンパイルします(templateのcoulpleを追加します;以下の「p.s.」を参照)。しかし、私は複雑すぎると思います。

p .: より前get()が私のclang ++によって要求されています。私のg ++​​はそれを必要とせず、それを受け入れます。私はあなたのバージョンにもそれを追加するべきだと思います。

p.s.2:悪い英語を申し訳ありません。

--- EDIT ---

は良く考えて、私の解決策は、あまりにも複雑オーバーです。単純

template<typename ObjectType> 
    ObjectType get() 
    { 
     return static_cast<_ObjectSpaceHolder<ObjectType> &>(*this).m_objectSpace.template get<ObjectType>(); 
    } 

について

何?

は、あなたの意図は、(あなたが文句を言わない、他の種類のソリューションを拡張する場合、およびその他のタイプ)を使用すると、型が可変長引数リストに含まれている場合は言う何かを書くことができObjectTypeGroupAまたはGroupBであることを確認する場合には、

template <typename T0> 
constexpr bool typeIsInList() 
{ return false; } 

template <typename T0, typename T1, typename ... Tl> 
constexpr bool typeIsInList() 
{ return std::is_same<T0, T1>::value || typeIsInList<T0, Tl...>(); } 

ObjectTypeGroupAGroupBによってconstituedリストにあること(SFINAEを経由して)確認するためにget()を再定義するようなもの。

template<typename ObjectType, typename = typename std::enable_if<typeIsInList<ObjectType, GroupA, GroupB>()>::type> 
    ObjectType get() 
    { 
     return static_cast<_ObjectSpaceHolder<ObjectType> &>(*this).m_objectSpace.template get<ObjectType>(); 
    } 
+0

これを見ていただきありがとうございますが、私の意図がはっきりしていないようですので、それに応じて回答を更新します。要約すると、特定のObjectTypeを照会する場合、getはGroupAオブジェクト・スペース所有者またはGroupBのいずれかにコールをディスパッチする必要があります。これらの呼び出しのうちの1つだけが有効であることが保証されています。だから私はこの条件付きディスパッチを表現する方法を探しています。 – OnMyLittleDuck

+0

残念ながら私は英語を正しく話さないので、それは確かに私のせいだし、あなたが十分にはっきりしていないとは言えない。申し訳ありません。しかし、あなたの新しい説明から私が理解していることは、私が以前のことから理解したものと一致します。今私が理解していないのは、私が提案した仮説が適切であるかどうかと、そうでない場合はなぜですか。 – max66

1

あなたがC++ 14、static_ifを使用することができた場合は、きれいな解決策のようになります。

template<typename ObjectType> 
auto get() 
{ 
    using is_group_a = std::is_same 
    < 
     ObjectType, 
     decltype(std::declval<GroupA>().template get<ObjectType>()) 
    >; 

    return static_if(is_group_a{}) 
     .then([](auto& x_this) 
     { 
      return static_cast<_ObjectSpaceHolder<GroupA> &>(x_this) 
       .m_objectSpace.get<ObjectType>(); 
     }) 
     .else_([](auto& x_this) 
     { 
      return static_cast<_ObjectSpaceHolder<GroupB> &>(x_this) 
       .m_objectSpace.get<ObjectType>();   
     })(*this); 
} 

両方のブランチは、解析可能である必要があり、だけ取られた分岐が実際にインスタンス化されます。

私はMeeting C++ 2015用にa tutorial on static_ifと書いています。それがどのように動作し、独自の実装を記述するのに十分なはずです。

私はan implementation hereも書いています。

両方の実装は、this CppCoreGuidelines issueに基づいています。

関連する問題