2014-10-10 9 views
9

Wenn ich diesen einfachen Fall:Warum std :: bind account für Funktion Arity?

struct Foo 
{ 
    void bar(); 
    void baz(int); 
}; 

Es macht Sinn, dass dieses zusammenstellen würde:

Foo foo; 
auto f = std::bind(&Foo::bar, &foo); 

Aber warum bind so gestaltet sein würde, dass dies kompiliert:

auto g = std::bind(&Foo::baz, &foo); 

Ich kann f anrufen, aber ich kann nie anrufen g. Warum sollte man das kompilieren? Was ist der Grund mich hinter erfordern tun zu müssen:

auto g2 = std::bind(&Foo::baz, &foo, std::placeholders::_1); 

Ich kann den Platzhalter verstehen verwenden, wenn Sie mit zu verwirren wollen, die Argumente übergeben bekommen und in welcher Reihenfolge, aber warum haben nicht nur den Standard-Pass alle die Argumente in der richtigen Reihenfolge, ohne es zu spezifizieren?

+1

Wahrscheinlich, weil es keine Möglichkeit gibt, die Anzahl der Argumente zu erhalten einer anderen Funktion, was bedeutet, dass das generierte Funktionsobjekt * eine * Anzahl von Argumenten erlauben müsste, einschließlich keiner, was zu undefiniertem Verhalten führen würde, wenn (zum Beispiel) 'g' ohne Argumente" aufgerufen "würde. –

+6

Ich denke, die 'std :: placeholders' zwei Funktionen erfüllen, 1. wie Sie sie sagen, sie leiten die Argumente von der Quelle zur Senke, 2. sie angeben, wie viele Argumente erforderlich sind. – Niall

+3

Die Boost-Bibliotheken können mehr von der ursprünglichen Design-Grundidee haben, die mit "bind" zusammenhängt. – Niall

Antwort

3

Aber warum so würde binden gestaltet, dass diese kompiliert:
auto g = std::bind(&Foo::baz, &foo);
ich f nennen kann, aber ich kann immer g nicht nennen. Warum sollte man das kompilieren?

Die Boost.Bind FAQ sagt, dass Boost.Bind wird in der Regel solche Fehler bei „bind Zeit“ (das heißt auf der Linie, wo Sie bind nennen) diagnostizieren. Allerdings ist der Standard nicht verlangen, dass für std::bind, sondern es folgend hat im Benötigt Element für std::bind:

INVOKE (fd, w1, w2, ..., wN) (20.9.2) ist ein gültiger Ausdruck für einige Werte seines w1, w2, ..., wN, wo N == sizeof...(bound_args).

Das bedeutet, dass Ihr Code gegen die Vorbedingung der Funktion verstößt, was zu undefiniertem Verhalten führt. Die Standardbibliotheksimplementierung ist nicht verpflichtet, auf Vorbedingungsverletzungen zu prüfen, das ist Ihre Aufgabe. Der Bibliothek ist es auch nicht verboten, sie zu überprüfen, daher wäre es für eine Implementierung passend, sie abzulehnen, wie es Boost.Bind tut. Ich würde eine Anforderung an Ihren Bibliotheksverkäufer stellen, in der Sie aufgefordert werden, ungültige Bindungsausdrücke als "Quality of Implementation" -Verbesserung zu diagnostizieren, wo dies möglich ist.

warum nicht einfach nur den Standard-Pass alle die Argumente in der richtigen Reihenfolge, ohne sie angeben zu müssen?

Ich kann mir zwei Gründe vorstellen.

Zum einen das Verhalten der Wrapper Aufruf von bind erstellt ist Argumente fallen zu lassen, die zu einem Platzhalter nicht entsprechen, so können Sie x(1, 2, 3) nennen und haben es alle Argumente ignorieren und foo.bar() nennen. Dies ist Teil eines allgemeinen Musters, in dem Sie eine N-Arity-Funktion mit bind umschließen können, um einen Aufruf-Wrapper mit einer völlig anderen Arity zu erstellen, der Argumente hinzufügen, sie entfernen, einige an bestimmte gebundene Werte usw. anpassen kann.Es wäre unmöglich, x(1, 2, 3) Drop alle Argumente zu haben, wenn das Standardverhalten, wenn keine Platzhalter in der bind Ausdruck verwendet werden, war es, alle Argumente zu übermitteln.

Zweitens ist es konsequenter Sie benötigen immer explizit sein darüber, welche Argumente Sie in welcher Reihenfolge übergeben werden sollen. Im Allgemeinen wäre es nur sinnvoll, alle Argumente der Aufruf passieren, wenn es keine gebundenen Argumente sind, wie sonst sollte bind wissen, ob die Aufruf Argumente zu übergeben vor oder nach den gebundenen Argumente?

z.B. gegeben

struct X { 
    void f(int, int) { } 
} x; 
auto h = bind(&X::f, &x, 1); 
h(2); 

Sollte der Aufruf von h(2) Ergebnis in x.f(1, 2) oder x.f(2, 1)? Da das richtige Verhalten, wenn es Argumente gebunden ist nicht offensichtlich, automatisch weiterleitet, alle Argumente, wenn es keine Platzhalter waren verwendet nur dann wirklich sinnvoll, wenn es keine gebundenen Argumente (denn dann ist es keine Frage, ob die gebundenen Argumente sollte an erster Stelle oder letzte) Das ist ein ziemlich spezieller Fall. ein wesentliches Merkmal der API ändert mit diesem Sonderfall arbeitet von zweifelhaftem Wert sein würde, vor allem, wenn es den x(1, 2, 3) macht - zu erreichen>foo.bar() Fall unmöglich. Eine alternative Lösung besteht darin, weiterhin zu verlangen, dass die Benutzer explizit angeben, was sie wollen, aber explizit "alles weiterleiten", wie von Tomasz Kamiński in N4171 vorgeschlagen, der auf der nächsten C++ - Ausschusssitzung diskutiert wird Woche. Der _all Platzhalter löst das Problem der Entscheidung, ob die Anrufung Argumente vor oder nach den gebundenen Argumente kommen sollte, weil Sie explizit sagen kann, ob Sie bind(f, arg1, arg2, std::placeholders::_all) oder bind(f, std::placeholders::_all, arg1, arg2) oder sogar bind(f, arg1, std::placeholders::_all, arg2)

+0

Was denken Sie über die Diagnose ungültiger Bindungsausdrücke als QoI-Erweiterung? :) – Barry

+1

Gute Idee! Ich werde es untersuchen :) –

+0

GCC-Stamm überprüft jetzt die Arity von Bindeausdrücken, https://gcc.gnu.org/ml/gcc-patches/2014-11/msg00032.html –