Die Boost C++ Bibliotheken

Kapitel 21. Boost.Optional

Die Bibliothek Boost.Optional bietet mit boost::optional eine Klasse an, die für optionale Rückgabewerte verwendet werden kann. Damit sind Rückgabewerte von Funktionen gemeint, die unter Umständen kein Ergebnis zurückgeben können. Sehen Sie sich dazu Beispiel 21.1 an, in dem eine herkömmliche Lösung ohne Boost.Optional zum Tragen kommt.

Beispiel 21.1. Spezielle Werte zur Kennzeichnung optionaler Rückgabewerte
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>

int get_even_random_number()
{
  int i = std::rand();
  return (i % 2 == 0) ? i : -1;
}

int main()
{
  std::srand(static_cast<unsigned int>(std::time(0)));
  int i = get_even_random_number();
  if (i != -1)
    std::cout << std::sqrt(static_cast<float>(i)) << '\n';
}

Im Beispiel 21.1 wird eine Funktion get_even_random_number() verwendet, die eine gerade Zufallszahl zurückgeben soll. Dazu wird auf die Funktion std::rand() aus der Standardbibliothek zugegriffen. Die Implementation von get_even_random_number() ist recht naiv: Gibt std::rand() eine gerade Zufallszahl zurück, wird diese als Rückgabewert von get_even_random_number() weitergereicht. Ist die von std::rand() generierte Zufallszahl ungerade, wird -1 zurückgegeben.

So wie get_even_random_number() implementiert ist, bedeutet -1, dass keine gerade Zufallszahl generiert werden konnte und zurückgegeben werden kann. Die Funktion get_even_random_number() kann nicht garantieren, dass eine gerade Zufallszahl zurückgegeben wird. Der Rückgabewert ist optional.

Viele Funktionen verwenden spezielle Werte wie -1, um anzugeben, dass kein Ergebnis zurückgegeben werden kann. So gibt zum Beispiel die Methode find() der Klasse std::string den speziellen Wert std::string::npos zurück, wenn ein Substring nicht gefunden werden kann. Funktionen, deren Rückgabewert ein Zeiger ist, geben oft 0 zurück, um dem Aufrufer mitzuteilen, dass kein Ergebnis existiert.

Boost.Optional bietet mit boost::optional eine Klasse an, die optionale Rückgabewerte deutlich macht.

Beispiel 21.2. Optionale Rückgabewerte mit boost::optional
#include <boost/optional.hpp>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>

using boost::optional;

optional<int> get_even_random_number()
{
  int i = std::rand();
  return (i % 2 == 0) ? i : optional<int>{};
}

int main()
{
  std::srand(static_cast<unsigned int>(std::time(0)));
  optional<int> i = get_even_random_number();
  if (i)
    std::cout << std::sqrt(static_cast<float>(*i)) << '\n';
}

Im Beispiel 21.2 hat die Funktion get_even_random_number() mit boost::optional<int> einen neuen Typ für den Rückgabewert erhalten. Bei boost::optional handelt es sich um ein Template, das mit dem eigentlichen Typ des Rückgabewerts instanziiert werden muss. Um boost::optional verwenden zu können, muss außerdem die Headerdatei boost/optional.hpp eingebunden werden.

Generiert get_even_random_number() eine gerade Zufallszahl, wird diese mit return direkt zurückgeben. Sie wird automatisch in ein Objekt vom Typ boost::optional<int> gepackt, da boost::optional einen entsprechenden nicht-exklusiven Konstruktor anbietet. Wird keine gerade Zufallszahl generiert, wird ein leeres Objekt vom Typ boost::optional<int> zurückgegeben. Dieses wird über den Aufruf des Standardkonstruktors erstellt.

In der Funktion main() wird überprüft, ob i nicht leer ist. Ist dies der Fall, wird über operator* auf die Zahl zugegriffen, die in i gespeichert ist. boost::optional scheint daher ähnlich wie ein Zeiger zu funktionieren. Es ist jedoch wichtig, boost::optional nicht mit einem Zeiger gleichzusetzen, da zum Beispiel der Copy-Konstruktor bei boost::optional den im Objekt gespeicherten Wert kopiert, während ein Zeiger nicht den Wert kopiert, auf den er zeigt.

Beispiel 21.3. Verschiedene Methoden von boost::optional
#include <boost/optional.hpp>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>

using boost::optional;

optional<int> get_even_random_number()
{
  int i = std::rand();
  return optional<int>{i % 2 == 0, i};
}

int main()
{
  std::srand(static_cast<unsigned int>(std::time(0)));
  optional<int> i = get_even_random_number();
  if (i.is_initialized())
    std::cout << std::sqrt(static_cast<float>(i.get())) << '\n';
}

Beispiel 21.3 stellt verschiedene Methoden von boost::optional vor. So bietet die Klasse einen speziellen Konstruktor an, dem als ersten Parameter eine Bedingung übergeben werden kann. Ist die Bedingung wahr, wird ein Objekt vom Typ boost::optional mit dem zweiten Parameter initialisiert. Ist die Bedingung falsch, wird ein leeres Objekt vom Typ boost::optional erstellt. Dieser Konstruktor wird im Beispiel 21.3 in der Funktion get_even_random_number() verwendet.

Über is_initialized() kann explizit überprüft werden, ob ein Objekt vom Typ boost::optional nicht leer ist. Boost.Optional spricht von initialisierten und nicht-initialisierten Objekten – daher der Name der Methode is_initialized(). Die Methode get() wiederum ist gleichbedeutend mit operator*.

Beispiel 21.4. Verschiedene Hilfsfunktionen von Boost.Optional
#include <boost/optional.hpp>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>

using namespace boost;

optional<int> get_even_random_number()
{
  int i = std::rand();
  return make_optional(i % 2 == 0, i);
}

int main()
{
  std::srand(static_cast<unsigned int>(std::time(0)));
  optional<int> i = get_even_random_number();
  double d = get_optional_value_or(i, 0);
  std::cout << std::sqrt(d) << '\n';
}

Boost.Optional bietet freistehende Hilfsfunktionen wie boost::make_optional() und boost::get_optional_value_or() an. Während boost::make_optional() verwendet werden kann, um ein Objekt vom Typ boost::optional zu erzeugen, kann boost::get_optional_value_or() aufgerufen werden, wenn ein alternativer Wert verwendet werden soll, sollte boost::optional leer sein.

Die Funktion get_optional_value_or() wird auch als Methode von boost::optional angeboten. Sie heißt dann get_value_or().

Boost.Optional bietet mit boost/optional/optional_io.hpp auch eine Headerdatei an, die Stream-Operatoren überlädt, um Objekte vom Typ boost::optional zum Beispiel direkt auf die Standardausgabe ausgeben zu können.