Die Boost C++ Bibliotheken

Kapitel 22. Boost.Tuple

Die Bibliothek Boost.Tuple stellt eine Klasse boost::tuple zur Verfügung, die als eine verallgemeinerte Version von std::pair bezeichnet werden kann. Während std::pair genau zwei Werte speichert, kann boost::tuple beliebig viele Werte speichern.

Seit C++11 bietet die Standardbibliothek die Klasse std::tuple an. Arbeiten Sie in einer C++11-Entwicklungsumgebung, können Sie Boost.Tuple ignorieren, da boost::tuple und std::tuple gleich sind.

Beispiel 22.1. boost::tuple als std::pair-Ersatz
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <string>
#include <iostream>

int main()
{
  typedef boost::tuple<std::string, int> animal;
  animal a{"cat", 4};
  std::cout << a << '\n';
}

Um die Klasse boost::tuple verwenden zu können, muss die Headerdatei boost/tuple/tuple.hpp eingebunden werden. Möchten Sie Variablen vom Typ boost::tuple von Streams lesen und auf Streams ausgeben, müssen Sie außerdem die Headerdatei boost/tuple/tuple_io.hpp einbinden. Boost.Tuple stellt keine Master-Headerdatei zur Verfügung, die automatisch alle anderen Headerdateien einbindet.

boost::tuple wird grundsätzlich genauso verwendet wie std::pair. So können Sie wie im Beispiel 22.1 zwei Template-Parameter angeben, um zwei Werte zu speichern – in diesem Fall einen vom Typ std::string und einen vom Typ int. Die Typdefinition heißt animal, weil damit der Name und die Anzahl der Beine von Tieren angegeben werden können sollen.

Während die obige Typdefinition von animal auch mit std::pair hätte erfolgen können, ist es möglich, Objekte vom Typ boost::tuple in einen Stream zu schreiben. Dazu muss wie bereits erwähnt die Headerdatei boost/tuple/tuple_io.hpp eingebunden werden, in der sich die Definitionen der entsprechenden Stream-Operatoren befinden. So gibt Beispiel 22.1 (cat 4) aus.

Beispiel 22.2. boost::tuple als das bessere std::pair
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <string>
#include <iostream>

int main()
{
  typedef boost::tuple<std::string, int, bool> animal;
  animal a{"cat", 4, true};
  std::cout << std::boolalpha << a << '\n';
}

Beispiel 22.2 unterstützt zusätzlich zum Namen und zur Anzahl der Beine einen Wahrheitswert, der angibt, ob ein Tier einen Schwanz hat. Alle drei Werte können in einem Tuple untergebracht werden. Wenn Sie das Programm ausführen, wird (cat 4 true) ausgegeben.

So wie es zu std::pair eine Hilfsfunktion std::make_pair() gibt, kann ein Tuple mit Hilfe einer Funktion boost::make_tuple() erstellt werden. Sehen Sie sich dazu Beispiel 22.3 an.

Beispiel 22.3. Tuple erstellen mit boost::make_tuple()
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <iostream>

int main()
{
  std::cout.setf(std::ios::boolalpha);
  std::cout << boost::make_tuple("cat", 4, true) << '\n';
}

Es ist auch möglich, ein Tuple zu erstellen, das Referenzen enthält. Sehen Sie sich dazu Beispiel 22.4 an.

Beispiel 22.4. Tuple mit Referenzen
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <boost/ref.hpp>
#include <string>
#include <iostream>

int main()
{
  std::string s = "cat";
  std::cout.setf(std::ios::boolalpha);
  std::cout << boost::make_tuple(boost::ref(s), 4, true) << '\n';
}

Während die Werte 4 und true als Kopie übergeben und direkt im Tuple gespeichert werden, ist das erste Element eine Referenz auf den String s. Um eine derartige Referenz zu erstellen, wird auf boost::ref() aus Boost.Ref zugegriffen. Entsprechend muss auf boost::cref() zugegriffen werden, wenn eine konstante Referenz erstellt werden soll.

Sie dürfen anstatt boost::ref() std::ref() aus der C++11-Standardbibliothek verwenden. Im obigen Beispiel muss jedoch boost::ref() zum Einsatz kommen, weil nur Boost.Ref einen Stream-Operator zur direkten Ausgabe auf die Standardausgabe unterstützt.

Nachdem Sie gesehen haben, wie Tuple erstellt werden, wird im Folgenden auf Elemente eines Tuples zugegriffen. Bei std::pair geschieht dies über die Eigenschaften first und second. Da ein Tuple jedoch keine festgelegte Anzahl an Elementen besitzt, findet der Zugriff auf eine andere Art und Weise statt.

Beispiel 22.5. Lesender Zugriff auf Werte in einem Tuple
#include <boost/tuple/tuple.hpp>
#include <string>
#include <iostream>

int main()
{
  typedef boost::tuple<std::string, int, bool> animal;
  animal a = boost::make_tuple("cat", 4, true);
  std::cout << a.get<0>() << '\n';
  std::cout << boost::get<0>(a) << '\n';
}

Es gibt zwei Möglichkeiten, auf ein Element in einem Tuple zuzugreifen: Entweder wird für das Tuple die Methode get() aufgerufen oder das Tuple als einziger Parameter an eine freistehende Funktion boost::get() übergeben. In beiden Fällen muss der Index des entsprechenden Elements im Tuple als Template-Parameter übergeben werden. Für Beispiel 22.5 bedeutet das, dass in beiden Fällen auf das erste Element im Tuple a zugegriffen wird und zweimal cat ausgegeben wird.

Wird ein ungültiger Index angegeben, kompiliert der Code nicht. Die Gültigkeit der Indizes wird zur Kompilierung überprüft, so dass es nicht zu Laufzeitfehlern kommen kann.

Um einen Wert in einem Tuple zu ändern, müssen Sie ebenfalls entweder auf die Methode get() oder die freistehende Funktion boost::get() zugreifen.

Beispiel 22.6. Schreibender Zugriff auf Werte in einem Tuple
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <string>
#include <iostream>

int main()
{
  typedef boost::tuple<std::string, int, bool> animal;
  animal a = boost::make_tuple("cat", 4, true);
  a.get<0>() = "dog";
  std::cout << std::boolalpha << a << '\n';
}

Sowohl get() als auch boost::get() geben eine Referenz zurück. Im Beispiel 22.6 wird entsprechend der Name geändert und vom Programm (dog 4 true) ausgegeben.

Boost.Tuple stellt nicht nur überladene Operatoren zur Verfügung, um Tuple auf einen Stream auszugeben oder von einem Stream zu lesen. Boost.Tuple definiert auch Vergleichsoperatoren. Wollen Sie Tuple vergleichen, müssen Sie jedoch mit boost/tuple/tuple_comparison.hpp eine zusätzliche Headerdatei einbinden.

Beispiel 22.7. Tuple vergleichen
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <string>
#include <iostream>

int main()
{
  typedef boost::tuple<std::string, int, bool> animal;
  animal a1 = boost::make_tuple("cat", 4, true);
  animal a2 = boost::make_tuple("shark", 0, true);
  std::cout << std::boolalpha << (a1 != a2) << '\n';
}

Beispiel 22.7 gibt true aus, da die beiden Tuple a1 und a2 verschieden sind.

Die Headerdatei boost/tuple/tuple_comparison.hpp enthält auch Definitionen anderer Vergleichsoperatoren wie operator>, die einen lexikographischen Vergleich durchführen.

Boost.Tuple unterstützt eine besondere Form von Tuple namens Tier. Ein Tier ist ein Tuple, dessen Elemente alle Referenzen sind. Es kann mit der Funktion boost::tie() erstellt werden.

Beispiel 22.8. Ein Tier mit boost::tie() erstellen
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <string>
#include <iostream>

int main()
{
  typedef boost::tuple<std::string&, int&, bool&> animal;
  std::string name = "cat";
  int legs = 4;
  bool tail = true;
  animal a = boost::tie(name, legs, tail);
  name = "dog";
  std::cout << std::boolalpha << a << '\n';
}

Im Beispiel 22.8 wird ein Tier a erstellt, das aus Referenzen auf die Variablen name, legs und tail besteht. Indem die Variable name auf einen neuen Wert gesetzt wird, wird gleichzeitig das Tier geändert.

Anstatt boost::tie() zu verwenden, hätte Beispiel 22.8 auch mit boost::make_tuple() und boost::ref() geschrieben werden können. Sehen Sie sich dazu Beispiel 22.9 an.

Beispiel 22.9. Ein Tier ohne boost::tie() erstellen
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <string>
#include <iostream>

int main()
{
  typedef boost::tuple<std::string&, int&, bool&> animal;
  std::string name = "cat";
  int legs = 4;
  bool tail = true;
  animal a = boost::make_tuple(boost::ref(name), boost::ref(legs),
    boost::ref(tail));
  name = "dog";
  std::cout << std::boolalpha << a << '\n';
}

boost::tie() verkürzt die Schreibweise. Die Funktion bietet sich auch an, wenn Werte aus einem Tuple entpackt werden soll. Sehen Sie sich dazu Beispiel 22.10 an, in dem eine Funktion ein Tuple zurückgibt und die einzelnen Werte im Tuple sofort in Variablen gespeichert werden.

Beispiel 22.10. Rückgabewerte einer Funktion aus einem Tuple entpacken
#include <boost/tuple/tuple.hpp>
#include <string>
#include <iostream>

boost::tuple<std::string, int> new_cat()
{
  return boost::make_tuple("cat", 4);
}

int main()
{
  std::string name;
  int legs;
  boost::tie(name, legs) = new_cat();
  std::cout << name << ", " << legs << '\n';
}

Mit Hilfe von boost::tie() werden der String cat und die Zahl 4, die beide zusammen in einem Tuple von new_cat() zurückgegeben werden, direkt in den Variablen name und legs gespeichert.