Kein Safe-Bool Idiom?
« | 29 May 2022 | »Das Safe-Bool Idiom wurde früher jedem eingetrichtert, der daran dachte eigene Smart-Pointer Strukturen zu bilden.
Doch plötzlich, mit dem Aufkommen von C++11 und seinen Nachfolgern, verschwand dieses Mysterium wieder …
Objekte sind (besonders im System-nahen Bereich) oft nur Wrapper um native
Konstrukte (z.B. aus der C Welt).
Man nennt solche Dinge auch gerne “Smart-Handles” als Anlehnung an
Smart-Pointer und viele von ihnen haben die Eigenschaft, dass sie auch
“leer” oder “ungültig” sein können.
Und deshalb hätte man gerne, dass solche Objekte möglichst einfach
“ausge-if
-t” werden können, so wie das hier:
Das ist eigentlich alles kein Problem, denn da schreibt man einfach:
Und schon klappt der if
Aufruf.
Doch unglücklicherweise klappt dann noch viel mehr, wie z.B.:
Und schon regiert das absolute Chaos.
Safe Bool über exotische Pointer
Weil bool
sich in jeden anderen Integer konvertieren lässt, kam der Gedanke
auf, dass man einen anderen Typen braucht, der auch gegen true
/ false
auswertbar ist aber nicht automatisch in Rechnungen oder Vergleichen aktiv
wird.
Die Lösung fand man in einem an den Typen gebunden Methoden-Pointer, der
im true
Fall auf eine existierende private Methode zeigt, und im false
Fall ein NULL
-Pointer wird.
Unser if(handle)
findet dann einen “primitiven” Pointer-Typen, der
ausge-if
-t werden kann und nutzt ihn “implizit”.
Wenn man das ganze jetzt noch in eine Template-Klasse wirft, die als
Basisklasse eines “aus-if
-barem” Objektes genutzt wird, landet man bei der
Implementierung, die ich im GATE Framework immer noch einsetze um C++98/03
kompatibel zu sein.
Man kann z.B.: den safe-bool cast immer auf operator!()
umleiten und diesen
implementieren.
Dann hat man sowohl if(x)
und if(!x)
erledigt:
1template<class T> class SafeBoolBase 2{ 3private: 4 void safe_bool_method() const { } 5 6public: 7 typedef void(SafeBoolBase::*bool_type)() const; 8 9 operator bool_type() const 10 { 11 T const& derived_impl = *static_cast<T const*>(this); 12 return !derived_impl ? 0 : &SafeBoolBase<T>::safe_bool_method; 13 } 14}; 15 16class MyIfableType : public SafeBoolBase<MyIfableType> 17{ 18public: 19 bool operator!() const 20 { 21 ... 22 } 23};
C++11 und explicit operator bool()
In C++11 ist dieser verdrehte Pointer-Kunstgriff nicht mehr notwendig, denn
eine Funktion explicit operator bool() const
bedient nur noch wirklich
explizit casts oder eben if(x)
, lässt aber Rechnungen oder Vergleiche
hart abblitzen.
Vor C++11 war explicit
nur dem Konstruktor vorbehalten und konnte nicht auf
Operatorfunktionen angewendet werden. Konvertierungen waren damals also immer
“implizit”.
Die explizite Operatorfunktion macht sogar den bool operator!() const
in
einige Szenarien unnötig, denn ein
if(!x)
landet ebenfalls im explicit operator bool() const
und dreht sein
Ergebnis einfach um.
Fazit
Das Safe-Bool Idiom gerät bereits in Vergessenheit, denn junge Programmierer kennen es schlicht weg nicht.
Und obwohl ich die Eleganz des neueren Standards hier sehr schätze, hat das
Safe-Bool Idiom trotzdem einen hohen Lehrwert:
Genau weil ich damals vor 15 Jahren über implizite Konvertierungen
von operator bool
gestolpert bin, wurden mir viele Details des Typensystems
bewusst, die einem sonst verborgen bleiben.
Daher meine Empfehlung an alle:
Lernt beide Welten kennen!
Die alte wie die neue.
Nur so kann man Verständnis und eine bessere Zukunft aufbauen.