Warum std::bind über Lambdas in C++14 verwenden?

Lesezeit: 7 Minuten

Warum stdbind uber Lambdas in C14 verwenden
Ralf Tandetzky

Vor C++11 habe ich verwendet boost::bind oder boost::lambda viel. Die bind Teil hat es in die Standardbibliothek geschafft (std::bind) wurde der andere Teil Teil der Kernsprache (C++ Lambdas) und machte die Verwendung von Lambdas viel einfacher. Heutzutage benutze ich kaum noch std::bind, da ich mit C++-Lambdas fast alles machen kann. Es gibt einen gültigen Anwendungsfall für std::bind das fällt mir ein:

struct foo
{
  template < typename A, typename B >
  void operator()(A a, B b)
  {
    cout << a << ' ' << b;
  }
};

auto f = bind(foo(), _1, _2);
f( "test", 1.2f ); // will print "test 1.2"

Das C++14-Äquivalent dafür wäre

auto f = []( auto a, auto b ){ cout << a << ' ' << b; }
f( "test", 1.2f ); // will print "test 1.2"

Viel kürzer und prägnanter. (In C ++ 11 funktioniert dies aufgrund der Auto-Parameter noch nicht.) Gibt es einen anderen gültigen Anwendungsfall für std::bind die C++-Lambdas-Alternative schlagen oder ist std::bind überflüssig bei C++14?

  • Ich dachte, dass man Lambdas schon vorziehen sollte bind wo immer das sinnvoll war.

    – Bartek Banachewicz

    28. Juni 2013 um 10:55 Uhr

  • Schnittstelle mit fremdem (zB C) Code?

    – didierc

    28. Juni 2013 um 10:56 Uhr


  • @BartekBanachewicz die Frage ist was ist das wo auch immer.

    – didierc

    28. Juni 2013 um 10:59 Uhr

  • Lambdas können eingebunden werden – Bindungen nicht

    – Arztliebe

    28. Juni 2013 um 11:01 Uhr

  • Das C++11-Beispiel benötigt nicht einmal a bind. Benutz einfach auto f = foo{};

    – Aschepler

    28. Juni 2013 um 11:01 Uhr

1646717411 547 Warum stdbind uber Lambdas in C14 verwenden
BertR

Scott Meyers gab a sprechen darüber. Das ist, woran ich mich erinnere:

In C++14 gibt es nichts nützliches bind, was nicht auch mit Lambdas möglich wäre.

In C++11 Es gibt jedoch einige Dinge, die mit Lambdas nicht ausgeführt werden können:

  1. Sie können die Variablen während der Erfassung beim Erstellen der Lambdas nicht verschieben. Variablen werden immer als Lvalues ​​erfasst. Für bind kannst du schreiben:

    auto f1 = std::bind(f, 42, _1, std::move(v));
    
  2. Ausdrücke können nicht erfasst werden, nur Bezeichner. Für bind kannst du schreiben:

    auto f1 = std::bind(f, 42, _1, a + b);
    
  3. Überladen von Argumenten für Funktionsobjekte. Dies wurde bereits in der Frage erwähnt.

  4. Perfekt-Forward-Argumente sind nicht möglich

In C++14 all dies möglich.

  1. Bewegungsbeispiel:

    auto f1 = [v = std::move(v)](auto arg) { f(42, arg, std::move(v)); };
    
  2. Ausdrucksbeispiel:

    auto f1 = [sum = a + b](auto arg) { f(42, arg, sum); };
    
  3. Siehe Frage

  4. Perfekte Weiterleitung: Sie können schreiben

    auto f1 = [=](auto&& arg) { f(42, std::forward<decltype(arg)>(arg)); };
    

Einige Nachteile der Bindung:

  • Bind bindet nach Namen und wenn Sie mehrere Funktionen mit demselben Namen haben (überladene Funktionen), weiß bind nicht, welche zu verwenden ist. Das folgende Beispiel wird nicht kompiliert, während Lambdas damit kein Problem hätten:

    void f(int); void f(char); auto f1 = std::bind(f, _1, 42);
    
  • Bei der Verwendung von bind-Funktionen ist es weniger wahrscheinlich, dass sie inline sind

Andererseits könnten Lambdas theoretisch mehr Template-Code generieren als bind. Da Sie für jedes Lambda einen einzigartigen Typ erhalten. Für bind nur, wenn Sie unterschiedliche Argumenttypen und eine andere Funktion haben (ich denke, dass es in der Praxis jedoch nicht sehr oft vorkommt, dass Sie mehrmals mit denselben Argumenten und Funktionen binden).

Was Jonathan Wakely in seiner Antwort erwähnte, ist eigentlich ein weiterer Grund, bind nicht zu verwenden. Ich verstehe nicht, warum Sie Argumente stillschweigend ignorieren wollen.

  • Gibt es eine Möglichkeit, variadische Template-Argumente in eine C++14-Closure zu verschieben? Etwas wie: template<typename F, typename ...Args> auto make_thunk(F&& f, Args&&... args) { return [a = std::move(args)] { f(a); }

    –Zendel

    19. Mai 2016 um 21:20 Uhr


  • Link zum Vortrag ist leider defekt

    – Ayxan Haqverdili

    17. April 2019 um 7:19 Uhr

  • Es gibt immer noch das, was Lambdas nicht können: codereview.stackexchange.com/questions/234887/… Obwohl wir jetzt verwenden können std::invoke dafür. Aber ist es genauso langsam?

    – Oliver Schönrock

    1. Januar 2020 um 1:46 Uhr

  • @Zendel das ist eigentlich ein guter Anwendungsfall für std::bind (sehen godbolt.org/z/WpZ6ZU als Beispiel), mit der Einschränkung, dass alle Vorlagenparameter f müssen beim Aufruf von make_thunk explizit angegeben werden. Sie sollten auch in der Lage sein, generische Lambdas in C++14 zu verwenden, um den gleichen Effekt zu erzielen (siehe Kommentar von @BertR hier).

    – Durchbruch

    5. April 2020 um 15:36 Uhr


Manchmal ist es einfach weniger Code. Bedenken Sie:

bool check(int arg1, int arg2, int arg3)
{
  return ....;
}

Dann

wait(std::bind(check,a,b,c));

gegen Lambda

wait([&](){return check(a,b,c);});

Ich denke, dass die Bindung hier einfacher zu lesen ist als das Lambda, das wie a aussieht https://en.wikipedia.org/wiki/Brainfuck

  • Beachten Sie, dass Sie die leere Klammer in der Lambda-Definition entfernen können.

    – Hugal31

    5. August 2020 um 11:47 Uhr

  • Sie sind nicht gleichwertig. Ihre Bindung erfasst nach Wert, aber Ihr Lambda nach Referenz. Im letzteren Fall sollten Sie besser nicht baumeln!

    – Unterstrich_d

    25. November 2021 um 22:23 Uhr

Für mich eine gültige Verwendung für std::bind soll klarstellen, dass ich eine Mitgliedsfunktion als Prädikat verwende. Das heißt, wenn ich nur eine Member-Funktion aufrufe, ist es bind. Wenn ich mit dem Argument zusätzliche Dinge mache (neben dem Aufruf einer Member-Funktion), ist es ein Lambda:

using namespace std;
auto is_empty = bind(&string::empty, placeholders::_1); // bind = just map member
vector<string> strings;
auto first_empty = any_of(strings.begin(), strings.end(), is_empty);

auto print_non_empty = [](const string& s) {            // lambda = more than member
    if(s.empty())                // more than calling empty
        std::cout << "[EMPTY]";  // more than calling empty
    else                         // more than calling empty
        std::cout << s;          // more than calling empty
};
vector<string> strings;
for_each(strings.begin(), strings.end(), print_non_empty);

  • Warum verwenden bind dafür, wenn Sie verwenden könnten mem_fn?

    – Jonathan Wakely

    28. Juni 2013 um 12:49 Uhr

  • Grundsätzlich der Bequemlichkeit halber; Sie können verwenden mem_fn dafür. ich denke an bind als “allgemeiner” mem_fn (und jetzt, dass diese Bindung zur Bibliothek hinzugefügt wurde, sehe ich keinen Grund, sie zu verwenden mem_fn). Ebenso “warum mem_fn verwenden, wenn Sie die Funktion direkt aufrufen können”? Ich denke ein bind Der Ausdruck drückt die Absicht (Member als getrenntes Prädikat binden) besser aus als ein Lambda.

    – utnapistim

    28. Juni 2013 um 12:53 Uhr


  • Häh? mem_fn wurde gleichzeitig zur Sprache hinzugefügt binddenkst du an mem_fun? Ihr Beispiel sollte sein auto is_empty = std::mem_fn(&string::empty);das ist einfacher, bequemer und bringt die Absicht klarer zum Ausdruck.

    – Jonathan Wakely

    28. Juni 2013 um 12:55 Uhr


  • Ich dachte an mem_fun (und wusste nicht, dass mem_fun != mem_fn). Der Code sieht tatsächlich einfacher aus. BIS …

    – utnapistim

    28. Juni 2013 um 12:56 Uhr

Ein weiterer Unterschied besteht darin, dass zu bindende Argumente kopiert oder verschoben werden müssen, während ein Lambda Variablen verwenden kann, die als Referenz erfasst wurden. Siehe Beispiel unten:

#include <iostream>
#include <memory>

void p(const int& i) {
    std::cout << i << '\n';
}

int main()
{
    std::unique_ptr<int> f = std::make_unique<int>(3);

    // Direct
    p(*f);

    // Lambda ( ownership of f can stay in main )
    auto lp = [&f](){p(*f);};
    lp();

    // Bind ( does not compile - the arguments to bind are copied or moved)
    auto bp = std::bind(p, *f, std::placeholders::_1);
    bp();
}

Ich bin mir nicht sicher, ob es möglich ist, das Problem zu umgehen, indem Sie die obige Bindung verwenden, ohne die Signatur von zu ändern void p(const int&).

1646717412 210 Warum stdbind uber Lambdas in C14 verwenden
Orwellophil

Ich erweitere nur den Kommentar von @BertR zu dieser Antwort auf etwas Testbares, obwohl ich gestehe, dass ich mit std::forward<> keine Lösung zum Laufen bringen konnte.

#include <string>
#include <functional>
using namespace std::string_literals;

struct F {
    bool        operator()(char c, int  i) { return c == i;  };
    std::string operator()(char c, char d) { return ""s + d; };
};

void test() {
    { // using std::bind
        auto f = std::bind(F(), 'a', std::placeholders::_1);
        auto b = f(1);
        auto s = f('b');
    }
    { // using lambda with parameter pack
        auto x = [](auto... args) { return F()('a', args...); };
        auto b = x(1);
        auto s = x('b');
    }
}

Testen Sie bei Compiler-Explorer

972360cookie-checkWarum std::bind über Lambdas in C++14 verwenden?

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

Privacy policy