Die Boost C++ Bibliotheken

Kapitel 41. Boost.Bind

Boost.Bind ist eine Bibliothek, die etwas vereinfachen und verallgemeinern soll, was ursprünglich den Einsatz von std::bind1st() und std::bind2nd() voraussetzte. Diese beiden Funktionen wurden mit C++98 in die Standardbibliothek aufgenommen und erlauben es, Funktionen zu verknüpfen, selbst wenn ihre Signaturen nicht kompatibel sind. Das ist zum Beispiel ein Problem, wenn Funktionen mit Algorithmen aus der Standardbibliothek verwendet werden sollen, sie aber mehr Parameter erwarten als ihnen beim Aufruf vom Algorithmus übergeben werden.

Boost.Bind wurde mit C++11 in die Standardbibliothek aufgenommen. Wenn Ihre Entwicklungsumgebung C++11 unterstützt, finden Sie in der Headerdatei functional eine Funktion std::bind(). Die Probleme, die Boost.Bind und std::bind() lösen, können Sie jedoch je nach Anwendungsfall womöglich besser mit Lambda-Funktionen oder Boost.Phoenix lösen.

Beispiel 41.1. std::for_each() mit passender Funktion
#include <vector>
#include <algorithm>
#include <iostream>

void print(int i)
{
  std::cout << i << '\n';
}

int main()
{
  std::vector<int> v{1, 3, 2};
  std::for_each(v.begin(), v.end(), print);
}

std::for_each() erwartet als dritten Parameter eine Funktion oder ein Funktionsobjekt, die ihrerseits einen einzigen Parameter erwarten. Im Beispiel 41.1 übergibt std::for_each() die Zahlen im Container v nacheinander jeweils als einzigen Parameter an print().

Soll eine Funktion aufgerufen werden, deren Signatur nicht den Anforderungen eines Algorithmus entspricht, wird es schwierig. Möchten Sie zum Beispiel print() derart ändern, dass die Ausgabe nicht mehr standardmäßig auf std::cout erfolgt, sondern der Stream über einen zweiten Parameter an die Funktion übergeben werden kann, könnte print() nicht ohne Weiteres mit std::for_each() verwendet werden.

Beispiel 41.2. std::for_each() mit std::bind1st()
#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>

class print : public std::binary_function<std::ostream*, int, void>
{
public:
  void operator()(std::ostream *os, int i) const
  {
    *os << i << '\n';
  }
};

int main()
{
  std::vector<int> v{1, 3, 2};
  std::for_each(v.begin(), v.end(), std::bind1st(print{}, &std::cout));
}

Beispiel 41.2 gibt wie das vorherige alle Zahlen aus v auf die Standardausgabe aus. Diesmal ist es jedoch möglich, den Stream, auf den die Ausgabe erfolgen soll, als Parameter zu übergeben. Dies erforderte jedoch größere Änderungen: So musste die Funktion print() in ein Funktionsobjekt umgewandelt werden, das außerdem von std::binary_function abgeleitet werden muss.

Mit Boost.Bind kann eine einfachere Lösung gewählt werden, ohne print() von einer Funktion in ein Funktionsobjekt umwandeln zu müssen. Dazu wird auf die Template-Funktion boost::bind() zugegriffen, die in der Headerdatei boost/bind.hpp definiert ist.

Beispiel 41.3. std::for_each() mit boost::bind()
#include <boost/bind.hpp>
#include <vector>
#include <algorithm>
#include <iostream>

void print(std::ostream *os, int i)
{
  *os << i << '\n';
}

int main()
{
  std::vector<int> v{1, 3, 2};
  std::for_each(v.begin(), v.end(), boost::bind(print, &std::cout, _1));
}

Beispiel 41.3 verwendet print() als Funktion und nicht als Funktionsobjekt. Da print() zwei Parameter erwartet, kann die Funktion nicht direkt an std::for_each() übergeben werden. Stattdessen wird boost::bind() an std::for_each() übergeben. print() wiederum wird als erster Parameter an boost::bind() übergeben.

Da print() zwei Parameter erwartet, müssen zwei zusätzliche Parameter an boost::bind() übergeben werden. Das ist zum einen ein Zeiger auf std::cout, zum anderen die Angabe _1.

_1 ist ein Platzhalter, der in Boost.Bind definiert ist. Neben _1 sind weitere Platzhalter von _2 bis _9 definiert. Diese Platzhalter führen dazu, dass boost::bind() ein Funktionsobjekt zurückgibt, dem genauso viele Parameter übergeben werden müssen wie der Platzhalter mit der höchsten Zahl. Ist so wie im Beispiel 41.3 lediglich der Platzhalter _1 angegeben, gibt boost::bind() ein unäres Funktionsobjekt zurück – also ein Funktionsobjekt, das einen einzigen Parameter erwartet. Dies ist hier insofern notwendig, weil std::for_each() genau einen Parameter übergibt.

Wenn Sie das Beispiel ausführen, ruft std::for_each() ein unäres Funktionsobjekt auf. Der Wert, der an das unäre Funktionsobjekt übergeben wird – eine Zahl aus dem Container v – nimmt die Position des Platzhalters _1 ein. Der Zeiger auf std::cout und die Zahl werden als Parameter an die Funktion print() weitergereicht. Auf diese Weise sieht std::for_each() ein unäres Funktionsobjekt, das durch die Art und Weise, wie boost::bind() verwendet wird, definiert ist. boost::bind() ruft seinerseits die Funktion auf, die als erster Paramter angegeben ist, und übergibt den Zeiger auf std::cout und die Zahl im Platzhalter an diese Funktion.

Beachten Sie, dass boost::bind() so wie std::bind1st() und std::bind2nd() Parameter als Kopie übernimmt. Um in den obigen Beispielen zu verhindern, dass versucht wird, von std::cout eine Kopie zu erstellen, erwartet print() einen Zeiger auf einen Stream. Im Kapitel 42 lernen Sie eine Funktion kennen, die es Ihnen ermöglicht, Parameter als Referenz zu übergeben.

Im Folgenden sehen Sie ein Beispiel, in dem mit Hilfe von boost::bind() ein binäres Funktionsobjekt definiert wird. Dazu wird der Algorithmus std::sort() verwendet, der als dritten Parameter eine binäre Funktion erwartet.

Beispiel 41.4. std::sort() mit boost::bind()
#include <boost/bind.hpp>
#include <vector>
#include <algorithm>
#include <iostream>

bool compare(int i, int j)
{
  return i > j;
}

int main()
{
  std::vector<int> v{1, 3, 2};
  std::sort(v.begin(), v.end(), boost::bind(compare, _1, _2));
  for (int i : v)
    std::cout << i << '\n';
}

Im Beispiel 41.4 wird ein binäres Funktionsobjekt erstellt, weil mit _2 ein Platzhalter verwendet wird, der die Zwei im Namen trägt. Der Algorithmus std::sort() ruft dieses binäre Funktionsobjekt mit zwei Werten aus dem Container v auf und wertet den Rückgabewert aus, um den Container zu sortieren. So, wie die Funktion compare() definiert ist, wird v absteigend sortiert.

Da compare() eine binäre Funktion ist, könnte sie direkt an std::sort() übergeben werden. Der Einsatz von boost::bind() kann trotzdem Sinn ergeben, wenn zum Beispiel der Container aufsteigend sortiert werden soll, die Definition der Funktion compare() aber nicht geändert werden soll.

Beispiel 41.5. std::sort() mit boost::bind() und geänderter Platzhalterreihenfolge
#include <boost/bind.hpp>
#include <vector>
#include <algorithm>
#include <iostream>

bool compare(int i, int j)
{
  return i > j;
}

int main()
{
  std::vector<int> v{1, 3, 2};
  std::sort(v.begin(), v.end(), boost::bind(compare, _2, _1));
  for (int i : v)
    std::cout << i << '\n';
}

Im Beispiel 41.5 ist die Reihenfolge der Platzhalter geändert worden: _2 wird als erster und _1 als zweiter Parameter an compare() übergeben. Somit wird v aufsteigend sortiert.