2016-05-09 13 views
5

das folgende Stück Code Gegeben:Compile Zeit Versand: abhängig gültigen Aufruf

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

Im get() Anruf, würde Ich mag eine Kompilierung Absendung an den entsprechenden Handler auszuführen. Die zugrunde liegende Idee ist, dass ObjectType entweder durch GroupA oder GroupB bekannt ist. Mein erster Ansatz war die folgende:

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

mit:

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

ich angenommen hatte nachgedacht funktionieren würde, dass ObjectType in der is_same Klausel enable_if Substitution einer der Ausdrücke führen würde zu scheitern und deshalb verlassen nur eine einzige gültige Spezialisierung. Mehrdeutige Namen und Neudefinitionsfehler beweisen mich jedoch falsch.

Warum ist meine Argumentation falsch? Und wie kann ich den Anruf stattdessen richtig versenden?

+0

@JoachimPileborg: Danke, das war ein Schreibfehler bei der Vereinfachung der Namen. Das Problem wurde behoben. – OnMyLittleDuck

+2

Beachten Sie auch, dass '_ObjectSpaceHolder' für den Compiler reserviert ist (zusammen mit allen Namen, die Unterstrich-Großbuchstaben beginnen). –

+0

Ich nehme an, die Annahme in der obigen ist, dass nur eine der folgenden Vorlagen T GroupA :: get () 'und' template T GroupB :: get () 'exists - ist das definitiv wahr? – Smeeheey

Antwort

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

dies nimmt eine Vorlage und eine Liste von Argumenten, und sagt Ihnen, wenn Sie sie anwenden können.

Eine Vorlage, die foo.get<Bar>() auswertet:

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

Können wir rufen Sie die obige Vorlage gültig?

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

Ein Paket eine Vorlage in eine Art zu setzen, die uns lässt es bewerten:

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

Ein ähnliches Paket, das verwirft seine Argumente und kehrt Ergebnis immer:

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

Wertet get_template_result aus, wenn möglich. Wenn ja, vergleicht seinen Typ mit ObjectType. Ansonsten vergleicht ObjectType* mit ObjectType (garantiert false):

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

Sobald wir alles haben, können wir Versand Tag hinzu!

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

jetzt get_smart<ObjectType, TypeA, TypeB>(something) wird die Liste suchen TypeA dann TypeB, bis sie eine Art findet man .get<ObjectType>() auf und gibt ObjectType aufrufen können. Dann hört es auf.

Wenn kein solcher Typ gefunden wird, kann er nicht kompiliert werden.

Sie sind dafür verantwortlich, den r/l-Wert der Liste der Typen TypeA TypeB und ObjectType festzulegen. Die Länge der Liste ist durch die Rekursionsbegrenzung begrenzt (normalerweise in den 100ern).

+0

Wie ordentlich! Danke, habe viel daraus gelernt! – OnMyLittleDuck

0

Was

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

?

Ihre Annahme scheint mir rigth und ich kompilieren Sie Ihren Code (Hinzufügen eines coulple von template; siehe folgende „P. S.“), aber ich denke, ist zu kompliziert.

ps .: die template vor get() wird von meinem clang ++ angefordert; Mein g ++ benötigt es nicht, akzeptiert es aber. Ich nehme an, du solltest es auch deiner Version hinzufügen.

S.2: Entschuldigung für mein schlechtes Englisch.

--- EDIT ---

Denken besser, meine Lösung ist auch zu kompliziert.

Was ist ein einfacheres

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

?

Wenn Sie Absicht ist, um sicher zu sein, dass ObjectType ist GroupA oder GroupB (und andere Arten, wenn Sie nicht die Lösung auf andere Typen erweitern) könnten Sie etwas schreiben, die sagen, wenn ein Typ in einer variadische Liste ist; so etwas wie

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

und neu definieren get() sicher zu sein (über SFINAE), dass ObjectType in der Liste ist constitued von GroupA und GroupB; so etwas wie

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

Danke, dass Sie sich das angeschaut haben, aber es scheint, dass meine Absicht nicht klar war, also werde ich die Antwort entsprechend aktualisieren.Zusammenfassend: Wenn Sie nach einem bestimmten ObjectType gefragt werden, sollte get den Aufruf entweder an den GroupA-Objektrespeicher oder an den GroupB-Objekt absetzen. Es ist garantiert, dass nur einer dieser Aufrufe gültig ist. Also suche ich nach einer Möglichkeit, diese bedingte Sendung auszudrücken. – OnMyLittleDuck

+0

Leider spreche ich nicht richtig Englisch, also ist es sicherlich meine Schuld und es ist nicht wahr, dass Sie nicht klar genug sind. Das tut mir leid. Aber ... was ich von Ihrer neuen Erklärung verstehe, stimmt mit dem überein, was ich von der vorherigen verstanden habe. Was ich gerade jetzt nicht verstehe, ist, ob die Hypothesen, die ich vorgeschlagen habe, angemessen sind und wenn nicht, warum. – max66

1

Wenn Sie können Sie C++ verwenden, 14, sieht static_if wie eine saubere Lösung:

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

Beide Zweige parseable sein müssen, sondern nur die genommene Verzweigung tatsächlich instanziiert werden.

Ich habe a tutorial on static_if für Meeting C++ 2015 geschrieben. Es sollte ausreichen, um zu verstehen, wie es funktioniert und Ihre eigene Implementierung zu schreiben.

Ich habe auch an implementation here geschrieben.

Beide Implementierungen basieren auf this CppCoreGuidelines issue.