========================= SFINAE-basierte Techniken [TOC] ========================= Bei SFINAE (_substitution failure is not an error_) schließen wir Kandidaten aus dem Kandidatenpool für bestimmte Template-Parameter aus, wenn gewisse Template-Abhängigkeiten nicht erfüllt sind. Wie wir zuvor bereits gesehen haben, ist `decltype` hier ein sehr hilfreiches Werkzeug, da es nicht nur eingesetzt werden kann, um den Datentyp eines Ausdrucks zu bestimmen und an geeigneter Stelle einzufügen, sondern auch dazu genutzt werden kann, das Vorhandensein all der gewünschten Operatoren, Methoden und Funktionen sicherzustellen. Hier kommt diese Technik noch einmal zum Einsatz an einem etwas komplexeren Beispiel: :import: session05/step02/sum.hpp :import: session05/step02/sum.cpp ---- SHELL (path=session05/step02,hostname=theon) -------- g++ -Wall -o sum sum.cpp ./sum ---------------------------------------------------------- Warum wurde hier bei `decltype` der Ausdruck `*begin_it + *begin_it++` verwendet? Hätte es nicht auch `*begin_it` getan? Wir gewinnen damit folgende Vorteile: * Wir testen alle genutzte Operatoren, also die Dereferenzierung mit `*`, das Inkrementieren des Iterators mit `++` (hier als Post-Inkrement) und das Addieren der einzelnen Werte. * Der Typ von `*begin_it` ist eine _lvalue reference_. Beim `int`-Array im obigen Beispiel wäre dies `int&`. Die Referenz kann aufwändig mit `std::remove_reference` entfernt werden. Durch die Addition erzeugen wir aber implizit vom Typ her eine _rvalue_, d.h. wir erhalten dann `int` statt `int&`. Aufgabe ======= Entwickeln Sie eine Template-Funktion für das Skalarprodukt für zwei beliebige Container, die * von der zuvor entwickelten _dim_-Funktion unterstützt werden, * die gleiche Dimensionierung besitzen, * die durch-iterierbar sind und * deren Elemente miteinander multipliziert und addiert werden können. Welches der vier Kriterien ist zur Übersetzzeit nicht immer überprüfbar? Wie könnte das zur Laufzeit überprüft werden? Denken Sie daran, dass Sie bei `decltype` bequem mehrere Ausdrücke mit Hilfe des Komma-Operators verketten können. Der letzte Ausdruck bestimmt dann den Typ des `decltype`-Konstrukts. Achten Sie darauf, dass sowohl Container wie `std::vector` als auch traditionelle Arrays unterstützt werden, d.h. Sie können sich nicht darauf verlassen, dass Methoden _begin_ und _end_ existieren. Eine Abhilfe bieten hier die Funktionen `std::begin` und `std::end` aus `#include `, die sowohl für Objekte mit den entsprechenden Funktionen als auch Zeiger funktionieren. Sie können sich überlegen, wie Sie durch die beiden Container durchiterieren. Wenn Sie möchten, können Sie darauf bestehen, dass Sie einen _random access iterator_ haben. Prinzipiell ist es aber auch möglich, ohne _dim_ und ohne einen _random access iterator_ auszukommen. Folgendes Testprogramm sollte dann funktionieren: :import: session05/step02/testit.cpp :navigate: up -> doc:index back -> doc:session05/page02 next -> doc:session05/page04