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.
-
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
-
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&&...) { }