Wenn ich diesen einfachen Fall habe:
%Vor%Es macht Sinn, dass dies kompilieren würde:
%Vor% Aber warum sollte bind
so entworfen werden, dass dies Folgendes kompiliert:
Ich kann f
aufrufen, aber ich kann niemals g
aufrufen. Warum sollte man das kompilieren? Was ist der Grund dafür, dass ich etwas tun muss?
Ich kann die Verwendung der Platzhalter verstehen, wenn Sie mit umgehen wollen, welche Argumente übergeben werden und in welcher Reihenfolge, aber warum nicht einfach die default pass alle Argumente haben die richtige Reihenfolge, ohne sie anzugeben?
Aber warum sollte bind so entworfen werden, dass dies kompiliert:
auto g = std::bind(&Foo::baz, &foo);
Ich kannf
aufrufen, aber ich kann niemalsg
aufrufen. Warum sollte man das kompilieren?
Die Boost.Bind FAQ sagt, dass Boost.Bind dies tut diagnostizieren solche Fehler normalerweise bei "Bindezeit" (dh in der Zeile, in der Sie bind
aufrufen). Der Standard erfordert dies jedoch nicht für std::bind
, sondern stattdessen für das Element Requires für std::bind
:
INVOKE (fd, w1, w2, ..., wN)
(20.9.2) soll ein gültiger Ausdruck für einige Werte sein w1, w2, ..., wN , wobeiN == sizeof...(bound_args)
.
Dies bedeutet, dass Ihr Code die Vorbedingung der Funktion verletzt, 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 haben Sie nicht einfach die Argumente pass all in der richtigen Reihenfolge, ohne sie anzugeben?
Ich kann mir zwei Gründe vorstellen.
Erstens ist das Verhalten des Call-Wrappers, der von bind
erstellt wurde, das Löschen von Argumenten, die keinem Platzhalter entsprechen. Daher können Sie x(1, 2, 3)
aufrufen und alle Argumente ignorieren und foo.bar()
aufrufen. Dies ist Teil eines allgemeinen Schemas, in dem Sie eine N-Arity-Funktion mit bind
umschließen können, um einen Call-Wrapper mit einer völlig anderen Arity zu erstellen, der Argumente hinzufügen, sie entfernen, einige an bestimmte gebundene Werte usw. anpassen kann wenn x(1, 2, 3)
alle Argumente löscht, wenn das Standardverhalten, wenn keine Platzhalter im Bind-Ausdruck verwendet werden, das Weiterleiten aller Argumente ist.
Zweitens ist es konsequenter, immer explizit anzugeben, welche Argumente in welcher Reihenfolge übergeben werden sollen. Im Allgemeinen würde es nur Sinn machen, alle Aufrufargumente zu übergeben, wenn keine gebundenen Argumente vorhanden sind. Wie sollte sonst bind
wissen, ob die Aufrufargumente vor oder nach den gebundenen Argumenten übergeben werden sollen?
z.B. gegeben
%Vor% Sollte der Aufruf von h(2)
in x.f(1, 2)
oder x.f(2, 1)
resultieren? Da das korrekte Verhalten bei eingebundenen Argumenten nicht offensichtlich ist, macht die automatische Weiterleitung aller Argumente, wenn keine Platzhalter verwendet wurden, nur dann Sinn, wenn es keine gebundenen Argumente gibt (weil dann keine Frage ist, ob die gebundenen Argumente zuerst oder zuletzt kommen sollen) Das ist ein ziemlich spezieller Fall. Das Ändern eines signifikanten Features der API, um mit diesem speziellen Fall zu arbeiten, wäre von fraglichem Wert, insbesondere wenn es den x(1, 2, 3)
- & gt; foo.bar()
ist unmöglich zu erreichen.
Eine alternative Lösung besteht darin, weiterhin zu verlangen, dass Benutzer explizit angeben, was sie wollen, aber eine explizite Möglichkeit bieten, "alles einfach weiterzuleiten", wie von Tomasz Kamiński in N4171 , die nächste Woche auf der C ++ - Ausschusssitzung diskutiert werden. Der Platzhalter _all
löst das Problem, zu entscheiden, ob die Aufrufargumente vor oder nach den gebundenen Argumenten stehen sollen, weil Sie explizit angeben können, 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)