Die Boost C++ Bibliotheken

Kapitel 49. Boost.EnableIf

Boost.EnableIf ermöglicht es, überladene Template-Funktionen oder spezialisierte Template-Klassen zu deaktivieren. Die Deaktivierung führt dazu, dass die entsprechenden Templates vom Compiler nicht berücksichtigt werden. Dies hilft nicht nur, Situationen zu vermeiden, in denen der Compiler nicht weiß, welche überladene Template-Funktion er heranziehen soll. Es macht es auch einfach, Templates zu definieren, die nicht nur für einen bestimmten Typ, sondern für eine Gruppe von Typen gelten sollen.

Boost.EnableIf ist seit C++11 Teil der Standardbibliothek. Sie können auf die in diesem Kapitel vorgestellten Funktionen ohne Boost-Bibliothek zugreifen, wenn Sie die Headerdatei type_traits einbinden.

Beispiel 49.1. Mit boost::enable_if Funktionen über ihren Rückgabewert überladen
#include <boost/utility/enable_if.hpp>
#include <type_traits>
#include <string>
#include <iostream>

template <typename T>
typename boost::enable_if<std::is_same<T, int>, T>::type create()
{
  return 1;
}

template <typename T>
typename boost::enable_if<std::is_same<T, std::string>, T>::type create()
{
  return "Boost";
}

int main()
{
  std::cout << create<std::string>() << '\n';
}

Beispiel 49.1 definiert eine Template-Funktion create(), die ein Objekt von dem Typ zurückgeben soll, der als Template-Parameter übergeben wird. Das Objekt wird innerhalb von create() initialisiert, so dass kein Parameter an create() übergeben werden muss und kann. Die Parameterlisten der beiden create()-Funktionen unterscheiden sich nicht. So gesehen handelt es sich nicht um eine überladene Funktion. Der Compiler würde einen Fehler melden, wenn nicht mit Hilfe von Boost.EnableIf eine der beiden Template-Funktionen aktiviert und die andere deaktiviert würde.

Boost.EnableIf stellt die Klasse boost::enable_if zur Verfügung. Es handelt sich um ein Template, das zwei Parameter erwartet. Der erste Parameter gibt eine Bedingung an. Der zweite Parameter ist der Typ des gesamten boost::enable_if-Ausdrucks, wenn die Bedingung wahr ist. Der Trick ist, dass dieser Typ nicht existiert, wenn die Bedingung falsch ist. Der boost::enable_if-Ausdruck ergibt in diesem Fall ungültigen Code. Bei Templates beschwert sich der Compiler jedoch nicht, wenn er auf ungültigen Code trifft. Stattdessen ignoriert er das entsprechende Template und sucht nach einem anderen, das passen könnte. Dieses Konzept ist auch als SFINAE bekannt, was für Substitution Failure Is Not An Error steht.

Im Beispiel 49.1 greifen beide Bedingungen im boost::enable_if-Ausdruck auf die Klasse std::is_same zu. Diese Klasse stammt aus der C++11-Standardbibliothek und ermöglicht den Vergleich zweier Typen. Da ein derartiger Vergleich entweder wahr oder falsch ist, reicht es aus, std::is_same zur Definition einer Bedingung zu verwenden.

Ist eine Bedingung wahr, soll die entsprechende create()-Funktion ein Objekt von dem Typ zurückgeben, der beim Aufruf von create() als Template-Parameter angegeben ist. Daher wird als zweiter Parameter an boost::enable_if der Template-Parameter T übergeben. Der gesamte boost::enable_if-Ausdruck wird durch T ersetzt, wenn eine Bedingung wahr ist. Der Compiler sieht im Beispiel 49.1 also entweder eine Funktion, die ein int zurückgibt, oder eine Funktion, die ein std::string zurückgibt. Sollte create() mit einem anderen Typ als int oder std::string aufgerufen werden, würde der Compiler einen Fehler melden.

Wenn Sie Beispiel 49.1 ausführen, wird Boost ausgegeben.

Beispiel 49.2. Mit boost::enable_if Funktionen für eine Gruppe von Typen spezialisieren
#include <boost/utility/enable_if.hpp>
#include <type_traits>
#include <iostream>

template <typename T>
void print(typename boost::enable_if<std::is_integral<T>, T>::type i)
{
  std::cout << "Integral: " << i << '\n';
}

template <typename T>
void print(typename boost::enable_if<std::is_floating_point<T>, T>::type f)
{
  std::cout << "Floating point: " << f << '\n';
}

int main()
{
  print<short>(1);
  print<long>(2);
  print<double>(3.14);
}

Beispiel 49.2 verwendet boost::enable_if, um eine Funktion für eine Gruppe von Typen zu spezialisieren. Die entsprechende Funktion heißt print() und erwartet einen Parameter. Sie könnte daher problemlos überladen werden. Beim Überladen muss jedoch ein konkreter Typ angegeben werden. Wenn die Funktion für eine Gruppe von Typen wie short, int und long das Gleiche tun soll, kann mit boost::enable_if eine entsprechende Bedingung definiert werden. Im Beispiel 49.2 wird hierzu auf std::is_integral zugegriffen. Die zweite print()-Funktion wird mit std::is_floating_point für alle Kommazahlen überladen – also für float und double.

Aufgabe

Vervollständigen Sie dieses Programm, so dass print_has_post_increment() ausgibt, ob ein Typ den Post-Inkrement-Operator unterstützt. Für int soll zum Beispiel int has a post increment operator ausgegeben werden:

#include <string>

template <class T>
void print_has_post_increment()
{
    // TODO: Implement this function.
}

int main()
{
    print_has_post_increment<int>();
    print_has_post_increment<long>();
    print_has_post_increment<std::string>();
}