default-Implementation für Funktionstemplates



  • Es wäre praktisch, wenn ich bei Funktionstemplates eine default-Implementierung angeben könnte, die nur benutzt wird, falls keine andere passt.

    Beispiel:

    /* Für container "c" Anzahl der Elemente in "r" reservieren */
    template <class C,class R> decltype(std::declval<C>().reserve(
        decltype(std::end(std::declval<R>()))() -
        decltype(std::begin(std::declval<R>()))()))
    reserve_if_possible(C& c,R&& r)
    {
      c.reserve(std::end(r)-std::begin(r));
    }
    
    /* die hier nur benutzen, falls obiges nicht passt */
    template <class C,class R> void reserve_if_possible(C& c,R&& r) {}
    
    [...]
    std::vector<int> a,b;
    reserve_if_possible(a,b);
    

    Falls C und R die Anforderungen erfüllen, passen beide Varianten und das ganze kompiliert nicht, weil der Aufruf mehrdeutig ist.
    Derartige Situationen habe ich öfter, aber wie löst man dies?



  • Suchst du SFINAE mittels std::enable_if ?



  • Ich kenne std::enable_if, bin mir aber nicht sicher, dass es das ist, was ich suche.
    Ich könnte das jetzt irgendwie so umschreiben, dass es enable_if benutzt, vielleicht so:

    /* Für container "c" Anzahl der Elemente im random access range "r" reservieren */
    template <class C,class R> typename std::enable_if<std::is_same<decltype(std::declval<C>().reserve(decltype(std::end(std::declval<R>()))()-decltype(std::begin(std::declval<R>()))())),void>::value,void>::type reserve_if_possible(C& c,R&& r)
    {
      c.reserve(std::end(r)-std::begin(r));
    }
    
    /* die hier nur benutzen, falls obiges nicht passt */
    template <class C,class R> typename std::enable_if<!std::is_same<decltype(std::declval<C>().reserve(decltype(std::end(std::declval<R>()))()-decltype(std::begin(std::declval<R>()))())),void>::value,void>::type reserve_if_possible(C& c,R&& r)
    {
    }
    

    Jetzt passen aber beide Varianten nicht, wenn C kein reserve() besitzt.
    Lasse ich die default-Variante wie vorher, dann ist es wieder ambiguous, falls C doch ein reserve() besitzt.


  • Mod

    so?

    #include <vector>
    #include <list>
    #include <iostream>
    struct adl_tag {};
    
    template <class C,class R> void reserve_if_possible_impl(C& c,R&& r, ...)
    {
      std::cout << "list\n";
    }
    
    template <class C,class R>
    auto reserve_if_possible(C& c,R&& r) -> decltype(reserve_if_possible_impl(c, std::forward<R>(r), adl_tag{}))
    {
      reserve_if_possible_impl(c, std::forward<R>(r), adl_tag{});
    }
    
    template <class C,class R>
    auto reserve_if_possible_impl(C& c,R&& r, adl_tag) -> decltype(c.reserve(std::end(r)-std::begin(r)))
    {
      c.reserve(std::end(r)-std::begin(r));
      std::cout << "vector\n";
    }
    
    int main()
    {
      int data[] = { 1, 2, 3 };
      std::vector<int> v;
      std::list<int> l;
      reserve_if_possible(v,data);
      reserve_if_possible(l,data);
    }
    


  • Ja, das ist perfekt! Wusste doch, dass es einen Trick gibt. Hoffentlich bin ich irgendwann mit Templates soweit vertraut, dass ich selbst auf solche Lösungen kommen kann.
    Danke sehr 🙂


  • Mod

    camper hat nicht gut gefrüstückt, hier ist die geilere Lösung:

    template <class C, class R>
    auto reserve_if_possible(C& c, R&& r) -> decltype( c.reserve(std::end(r) - std::begin(r)) )
    {
    	return c.reserve(std::end(r) - std::begin(r));
    }
    
    template <typename ... Args>
    void reserve_if_possible(Args&&...)
    {
    }
    

Anmelden zum Antworten