2016-05-09 13 views
5

考慮到下面這段代碼的條件:編譯時間調度:在有效的呼叫

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>(); 
     } 
    }; 

get()電話,我想進行編譯時分派到相應的處理。基本想法是ObjectType通過GroupAGroupB已知。我最初的做法是以下幾點:

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會導致其中的一個表達式失敗並因此而離開只有一個有效的專業化。然而,模棱兩可的名字和重新定義的錯誤證明了我的錯誤。

爲什麼我的推理不正確?我該如何恰當地派遣電話呢?

+0

@JoachimPileborg:謝謝,這是簡化名稱時引入的拼寫錯誤。修正了這個 – OnMyLittleDuck

+2

還要注意'_ObjectSpaceHolder'是爲編譯器保留的(以及所有以下劃線開頭的名字 - 大寫字母)。 –

+0

我想在上面的假設是隻有一個'模板 T GroupA ::得到()'和'模板 T GroupB ::得到()'存在 - 是否肯定是真的? – 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進行比較。否則,比較ObjectType*ObjectType(保證假):

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> 
>; 

一旦我們擁有了這一切,我們可以派遣標籤!

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)將搜索列表TypeA然後TypeB直到它找到一個類型,你可以在調用.get<ObjectType>()並返回ObjectType。然後停止。

如果找不到這種類型,則無法編譯。

您有責任設置類型TypeA TypeB和ObjectType的列表的r/l值。列表的長度受限於模板遞歸限制(通常在100s內)。

+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.s .: template之前get()是我的鏗鏘聲++所要求的;我的g ++不需要它,但接受它。我想你應該把它加到你的版本中。

p.s.2:抱歉我的英語不好。

---編輯---

思考更好的,我的解決辦法是過於複雜了。

什麼簡單的

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

如果你的目的是要確保ObjectTypeGroupAGroupB(和其他類型的,如果你不會擴展解決方案到其他類型的),你可以寫說,如果一個類型是一個可變參數列表的東西;像

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...>(); } 

,並重新定義get(),以確保(通過SFINAE),其ObjectType是由GroupAGroupB constitued列表;像

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對象。保證只有其中一個電話有效。所以我正在尋找一種方式來表達這種有條件的調度。 – 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