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?

Sie können ein Makro wie erstellen

#define FUNCTORIZE(func) [](auto&&... val) \
noexcept(noexcept(func(std::forward<decltype(val)>(val)...))) -> decltype(auto) \
{return func(std::forward<decltype(val)>(val)...);}

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


Benutzer-Avatar
Yakk – Adam Nevraumont

Hier ist ein Makro, das SFINAE, noexcept und Weiterleitung richtig ausführt:

#define RETURNS(...) \
  noexcept(noexcept(__VA_ARGS__)) \
  -> decltype(__VA_ARGS__) \
  { return __VA_ARGS__; }

#define CALLER_OF(...) \
  [](auto&&...args) \
  RETURNS( __VA_ARGS__(std::forward<decltype(args)>(args)...)) )

#define CALLER_OF_WITH_CAPTURE(...) \
  [&](auto&&...args) \
  RETURNS( __VA_ARGS__(std::forward<decltype(args)>(args)...)) )

auto constexpr predObj = CALLER_OF(pred);

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

Benutzer-Avatar
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);`

1012400cookie-checkDie knappste und wiederverwendbare Art, Vorlagen oder überladene Funktionen in Funktionsobjekte zu verpacken

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy