Value Templates

Wir wollen – inspiriert von Ada – gern einen bereichsgeprüften Integer-Datentyp entwickeln. Der naive Ansatz einer Klasse, die die Grenzen im Objekt speichert, funktioniert, ist aber etwas ineffizient. Value Templates können uns hier eine Hilfe sein, indem sie es uns ermöglichen, Objekte mit unterschiedlichen Grenzen auch zu unterschiedlichen Typen zu machen.

Video

Quellcode

#include <iostream>
#include <stdexcept>

template <int min, int max> class RangeInt
{
  int mValue;
  
  void check(int value)
  {
    std::cerr << "Check!\n";
    if (value < min || value > max) {
      throw std::out_of_range("Ungueltiger Wert");
    }
  }
  
public:
  RangeInt(int value)
  {
    check(value);
    mValue = value;
  }
  
  RangeInt(RangeInt const &other)
  {
    mValue = other.mValue;
  }
  
  RangeInt &operator=(RangeInt const &other)
  {
    mValue = other.mValue;
  }
  
  operator int() const
  {
    return mValue;
  }
};


int main()
{
  RangeInt<5, 20> r1(10);
  RangeInt<15, 30> r2(25);
  RangeInt<1, 10> r3(8);
  RangeInt<5, 20> r4(8);
  
  std::cerr << r1 << std::endl;
   r1 = r3;
   std::cerr << r1 << std::endl;
   r1 = r2;
   std::cerr << r1 << std::endl;
  r1 = r4;
  std::cerr << r1 << std::endl;
}

Erklärung

Der ursprüngliche Entwurf für einen bereichsgeprüften Integer war einfach: ein Feld für den Wert, zwei Felder für die Grenzen, ein paar passende Funktionen, fertig. Unser Problem: wir mussten für jede Zuweisung den Test ausführen, ob denn nun der Bereich verletzt wird oder nicht. Das ist eigentlich ja unnötig. Wir könnten auch einfach dafür sorgen, dass Zuweisungen nur dann funktionieren, wenn die Grenzen zweier Bereiche kompatibel sind. 

Zu unserer Rettung kommen hier Value Templates: Template-Klassen (und Funktionen), deren Parameterliste statt eines Platzhalters für einen Typ einen konkreten Typ enthält und die dann statt mit Typen gleich mit Werten instanziiert werden können. In unserem Fall ziehen wir die Grenzen des RangeInt in zwei Templateparameter. Damit erzeugen wir jedesmal für neue Grenzen auch einen neuen Typen. Da wir den Copy-Konstruktor und den Copy-Assignment-Operator so konstruiert haben, dass er nur Objekte gleichen Typs (und damit gleicher Grenzen) nimmt, können wir in diesen Fällen also immer sicher sein, dass der Wert passen muss und lassen die Bereichsprüfung einfach weg. Mit Erfolg: statt bei jeder Zuweisung eine Prüfung zu haben, haben wir die nun nur noch bei der Initialisierung.

Die Verwendung von Value Templates funktioniert äquivalent zu den Typtemplates: die konkreten Werte des Templateparameters stehen in der Liste hinter dem Namen des Templatetyps. Innerhalb der Templateklasse stehen die Werte unter den Namen der Template-Parameter überall zur Verfügung (zu sehen bspw. in Zeile 11 in der Funktion check).