Die knappste und wiederverwendbare Art, Vorlagen oder überladene Funktionen in Funktionsobjekte zu verpacken
Lesezeit: 4 Minuten
Szenario 1: eine Vorlagenfunktion pred
template<typename T>
bool pred(T t) { /* return a bool based on t */ }
Szenario 2: eine Reihe von Funktionen, die mit demselben Namen überladen sind pred
bool pred(A t) { /* return a bool based on t */ }
bool pred(B t) { /* return a bool based on t */ }
bool pred(C t) { /* return a bool based on t */ }
...
Egal in welchem der beiden Szenarien wir uns befinden, das Entscheidende ist das pred bezieht sich nicht auf eine Funktion und kann daher nicht herumgereicht werden, zB als unäres Prädikat an std::remove_if.
Daher ist es in diesem Fall praktisch, das folgende Objekt zu definieren, das stattdessen herumgereicht werden kann:
auto constexpr predObj = [](auto t){ return pred
Sobald ich jedoch ein ähnliches Bedürfnis nach einem anderen unären Prädikat habe, muss ich diese Zeile kopieren und einfügen und die beiden Namen in etwas anderes ändern; ähnlich, wenn ich das für ein binäres Prädikat tun muss:
auto contexpr binPredObj = [](auto x, auto y){ return binPred(x, y); };
Gibt es eine allgemeine Möglichkeit, dies automatisch zu machen? Ich denke an so etwas wie
auto funObj = fun2Obj(fun);
Ich habe das Gefühl, dass das, was ich verlange, gerade deshalb nicht möglich ist, weil es passieren müsste fun da es ein Funktionsobjekt war, was es nicht ist, sonst müsste ich kein Funktionsobjekt daraus machen. Aber fragen ist nie Verbrechen, oder?
Dadurch können Sie alle aufrufbaren Elemente in ein Closure-Objekt einschließen. Sie würden es gerne verwenden
auto constexpr predObj = FUNCTORIZE(pred);
Hallo, sorry für das späte Feedback, aber ich habe diese Frage vergessen. Ihre Antwort ist ausgezeichnet, wenn man bedenkt, dass ich nicht geschrieben habe ohne Verwendung von Makros. Würden Sie sagen, was ich verlange, ist einfach nicht ohne Makros möglich?
– Enliko
10. Februar 2021 um 19:18 Uhr
@Enlico Keine Sorge. Ohne Makro geht das glaube ich nicht. Sie können eine überladene Funktion nicht an eine andere Funktion übergeben (deshalb stellen Sie die Frage), so dass die Route aus ist, was AFAIK nur Makros als Option für etwas Automatisches übrig lässt. Makros sind nicht böse, sie haben legitime Verwendungen, wie dieser Anwendungsfall.
– NathanOliver
10. Februar 2021 um 19:21 Uhr
Vielen Dank. Als ich damals die Frage schrieb, wurde mir klar, dass ich mir im Grunde sagte, warum es nicht möglich ist, aber jemanden zu haben, der sachkundig ist, bestätigt, dass es immer gut ist! Akzeptiert.
– Enliko
10. Februar 2021 um 19:25 Uhr
@Enlico Gern geschehen. C++ ist ziemlich kompliziert, daher schadet es nie, eine zweite, dritte oder sogar vierte Meinung einzuholen. 🙂
– NathanOliver
10. Februar 2021 um 19:26 Uhr
Welche Bedeutung hat noexcept² hier?
– Enliko
23. Juni 2021 um 10:37 Uhr
Yakk – Adam Nevraumont
Hier ist ein Makro, das SFINAE, noexcept und Weiterleitung richtig ausführt:
da war ein Vorschlag um dies wie folgt in die Sprache zu integrieren:
auto constexpr predObj = (args...) => pred(std::forward<decltype(args)>(args)...);
aber das wurde abgelehnt.
CALLER_OF_WITH_CAPTURE (und @Barrys Vorschlag) hat eine Eigenart, dass der noexcept/decltype-Rückgabetest durchgeführt wird nicht im Zusammenhang mit irgendwelchen Erfassungenwährend der Körper fertig ist im Zusammenhang mit allen Erfassungen. So CALLER_OF_WITH_CAPTURE kann dabei überraschende Fehler aufweisen.
In CALLER_OFkann das Argument eine beliebige Reihe von Token sein, mit Ausnahme eines unausgeglichenen Abschlusses ).
Enliko
Nach einiger Zeit unterhielt ich mich mit einem Kollegen über dieses Thema, und er machte mich darauf aufmerksam, dass es eine Boost-Bibliothek gibt, die genau diese Funktionalität durch zwei Makros mit dem Namen bietet BOOST_HOF_LIFT und BOOST_HOF_LIFT_CLASS.
Beispiel:
#include <boost/hof.hpp>
#include <cassert>
#include <algorithm>
// Declare the class `max_f`
BOOST_HOF_LIFT_CLASS(max_f, std::max);
int main() {
auto my_max = BOOST_HOF_LIFT(std::max);
assert(my_max(3, 4) == std::max(3, 4));
assert(max_f()(3, 4) == std::max(3, 4));
}
Ebenfalls, hierist ein netter Blogbeitrag zu diesem Thema, der auch auf denselben Vorschlag hinweist, auf den die Antwort von Yakk – Adam Nevraumont hinweist.
Meine Frage bezieht sich auf Vorlagen und/oder überladene Funktionen, da dies eine “Kategorie” von “benannten Dingen” ist, die Sie nicht herumreichen können.
Es gibt jedoch noch eine andere Kategorie: Konstruktoren. Sie können keinen Konstruktor als Operator an die übergeben std::transform Algorithmus zum Beispiel.
Brunnen, Boost.Hof hilft auch in dieser Hinsicht über die boost::hof::construct Vorlage, die so ist, dass assert(construct<T>()(xs...) == T(xs...)); hält.
Es kann so verwendet werden
#include <boost/hof/construct.hpp>
struct A {
A(int) {} // some...
A(int, int) {} // ... constructors
};
auto makeA = boost::hof::construct<A>();
// now `makeA` is the "objectified" version of `A`, so you could use it like this:
std::transform(vecOfInts.begin(), vecOfInts.end(), vecOfAs.begin(), makeA);`
10124000cookie-checkDie knappste und wiederverwendbare Art, Vorlagen oder überladene Funktionen in Funktionsobjekte zu verpackenyes
This website is using cookies to improve the user-friendliness. You agree by using the website further.