2016-08-05 8 views
1

Dies ist möglich:Vorlagen Argumente nicht alle Anforderungen erfuellen,

struct A { 
    //void f(); < not declared in struct A 
}; 

template<typename T> 
struct Wrapper { 
    T t; 
    void call_f() { t.f(); } 
}; 

int main() { 
    Wrapper<A> w; 
} 

Diese zusammengestellt fein, solange w.call_f() wird nicht aufgerufen. w.call_f() kann nicht instanziiert werden, da A::f nicht existiert.

Ich habe eine Situation mit einer solchen Wrapper-Vorlage, die mit verschiedenen T Typen verwendet wird, die nicht immer alle Teile der Schnittstelle implementieren. (Hauptsächlich, um Code-Duplizierung zu vermeiden).

Dies funktioniert nicht:

struct A { 
    //using i_type = int; < not declared/defined 
}; 

template<typename T> 
struct Wrapper { 
    using its_i_type = typename T::i_type; 
     // compile error, even if `i_type` gets never used 
}; 

int main() { 
    Wrapper<A> w; 
} 

weder tut dies:

struct A { 
    //using i_type = int; < not declared/defined 
}; 

template<typename T> 
struct Wrapper { 
    typename T::i_type call_f() { return 0; } 
     // does not compile, even if `call_f()` is never instantiated 
}; 

int main() { 
    Wrapper<A> w; 
} 

Gibt es eine gute Möglichkeit, diese Situationen zu handhaben, ohne viel Code-Duplizierung (wie eine Spezialisierung für Wrapper, etc.)?

Antwort

3

Sie können den Typabzug its_i_type aufschieben. Im Grunde erstellen Sie einen einfachen Wrapper, den Sie durchlaufen müssen.


Um es auf andere Typen erweitern Sie benötigen, (ich wollte type_traits -ähnlichen Lösung vorzuschlagen, aber da Sie nicht Spezialisierungen wollen) Sie alle Typen, die Sie benötigen definieren könnte:

template<typename T> 
struct Wrapper { 
private: 
    template<typename U> struct i_typper { using type = typename U::i_type; }; 
    template<typename U> struct k_typper { using type = typename U::k_type; }; 
    template<typename U> struct p_typper { using type = typename U::p_type; }; 
public: 
    using i_trait = i_typper<T>; 
    using k_trait = k_typper<T>; 
    using p_trait = p_typper<T>; 
}; 

Beispiel:

struct A { using i_type = int; }; 
struct B { using i_type = int; using k_type = float; }; 

int main() { 
    Wrapper<A> w; //Works now. 

    Wrapper<A>::i_trait::type mk1; //Works 
    Wrapper<A>::k_trait::type mk2; //Fails, not defined 
    Wrapper<B>::i_trait::type mk3; //Works 
    Wrapper<B>::k_trait::type mk4; //Works 
} 

Für den Fall:

template<typename T> 
struct Wrapper { 
    typename T::i_type call_f() { return 0; } 
     // does not compile, even if `call_f()` is never instantiated 
}; 

Sie haben einige Möglichkeiten:

  1. machen, die eine Elementfunktion Vorlage funktionieren
  2. Verwenden irgendeine Form von type_traits Mechanismus, der noch Spezialisierung
  3. gehen den Weg zu abstrahieren gemeinsamen Wrapper beinhalten wird Sachen in einer Basisklasse WrapperBase;

Für die erste Option, Sie haben es ein wenig modifizieren defer deduction

template<typename T> 
struct Wrapper { 
private: 
    template<typename U, typename> struct i_typper { using type = typename U::i_type; }; 
    template<typename U, typename> struct k_typper { using type = typename U::k_type; }; 
    template<typename U, typename> struct p_typper { using type = typename U::p_type; }; 
public: 
    using i_trait = i_typper<T, void>; 
    using k_trait = k_typper<T, void>; 
    using p_trait = p_typper<T, void>; 

    template<typename U = void> 
    typename k_typper<T, U>::type call_f() { return 0; } 
}; 

ich die zweite Option als Übung werden lassen fördern: (es so etwas wie kann am Ende zu sein:

template<typename T> 
struct wrapper_traits { 
    .... 
}; 

template<> 
struct wrapper_traits<A>{ 
    using .... 
}; 

template<typename T> 
struct Wrapper { 
    .... 
public: 
    using i_trait = wrapper_traits<T>; 
    using k_trait = wrapper_traits<T>; 
    using p_trait = wrapper_traits<T>; 
}; 

Jarod's answer ist einfacher. Aber das wird funktionieren, wenn Sie keinen Zugang zu std::experimental haben, oder Ihre Firma Code Politik verbietet euch ...

+0

Und wie wärs mit einem 'call_f', die zurückgibt 'i_type'? – aschepler

+0

@Aschepler, dann wird es fehlschlagen. In diesem Fall werde ich entweder 'call_f' eine Template-Member-Funktion machen oder ich werde mit der 'type_trait'-ähnlichen Lösung gehen, die Spezialisierungen beinhaltet. – WhiZTiM

+0

*" Sie haben zwei Optionen [..] 1. [.. ] 2. [..] 3. [..] "* :-) (Ich weiß, Mehrfachbearbeitung). – Jarod42

2

Mit std::experimental::is_detected, können Sie tun

template<typename T> 
using i_type_t = typename T::i_type; 

template<typename T> 
struct Wrapper { 
    using its_i_type = typename std::experimental::detected_t<i_type_t, T>; 
     // would be T::i_type or std::experimental::nonesuch 
}; 

Oder besser in den Griff Fall, so etwas wie:

template<typename T, bool = std::experimental::is_detected<i_type_t, T>::value> 
struct WrapperWithIType { 
    // Empty for false case. 
}; 

template<typename T> 
struct WrapperWithIType<T, true> { 
    using its_i_type = i_type_t<T>; 

    its_i_type call_f() { return 0; } 
}; 

und dann

template<typename T> 
struct Wrapper : WrapperWithIType<T> { 
    // Common stuff 
}; 
+0

Eine Idee, warum 'std :: is_detected' in C++ 17 nicht akzeptiert wurde? ... – WhiZTiM

+0

@WhiZTiM es ist in einem TS (technische Spezifikation und arbeitet derzeit in g ++ - 6). – TemplateRex