Die Type-Traits-Bibliothek: Korrektheit
Die beiden Hauptziele der Type-Traits-Bibliothek sind sehr überzeugend: Korrektheit und Optimierung. Heute schreibe ich über die Korrektheit.
Die beiden Hauptziele der Type-Traits-Bibliothek sind sehr überzeugend: Korrektheit und Optimierung. Heute schreibe ich über die Korrektheit.
![Die Type-Traits Bibliothek: Korrektheit](https://heise.cloudimg.io/width/696/q85.png-lossy-85.webp-lossy-85.foil1/_www-heise-de_/imgs/18/3/2/3/0/6/8/6/templatesTypeTraits-2d5d787fcdcf5d0f.png)
Die Type-Traits-Bibliothek ermöglicht es, Typabfragen, Typvergleiche und Typmodifikationen zur Compiletime durchzuführen. In meinem letzten Artikel über die Type-Traits-Bibliothek [1] habe ich nur über Typabfragen und Typvergleiche geschrieben. Bevor ich über den Korrektheitsaspekt der Type-Traits-Bibliothek schreibe, möchte ich kurz auf Typmodifikationen eingehen.
Typmodifikationen
Die Type-Traits-Bibliothek bietet viele Metafunktionen zur Manipulation von Typen. Hier sind die Interessantesten:
// const-volatile modifications:
remove_const;
remove_volatile;
remove_cv;
add_const;
add_volatile;
add_cv;
// reference modifications:
remove_reference;
add_lvalue_reference;
add_rvalue_reference;
// sign modifications:
make_signed;
make_unsigned;
// pointer modifications:
remove_pointer;
add_pointer;
// other transformations:
decay;
enable_if;
conditional;
common_type;
underlying_type;
Um aus einem int
oder einem const int
einen int
zu erhalten, musst du mit ::type
nach dem Typ fragen.
std::is_same<int, std::remove_const<int>::type>::value; // true
std::is_same<int, std::remove_const<const int>::type>::value; // true
Seit C++14 kannst du einfach _t verwenden, um den Typ zu erhalten, wie bei std::remove_const_t:
std::is_same<int, std::remove_const_t<int>>::value; // true
std::is_same<int, std::remove_const_t<const int>>::value; // true
Um eine Vorstellung davon zu bekommen, wie nützlich diese Metafunktionen aus der Type-Traits-Bibliothek sind, hier ein paar Beispiele:
std::decay
:std::thread
wendetstd::decay
auf seine Argumente an. Zu den Argumenten vonstd::thread
gehören die ausgeführte Funktionf
und ihre Argumenteargs
. Decay bedeutet, dass implizite Konvertierungen von Array-zu-Zeiger, Funktion-zu-Zeiger durchgeführt werden und const/volatile-Qualifier und -Referenzen entfernt werden.std::enable_if
ist eine bequeme Möglichkeit, SFINAE zu verwenden. SFINAE steht für "Substitution Failure Is Not An Error" (Eine fehlgeschlagene Substitution ist kein Fehler) und wird bei der Überladung eines Funktions-Templates angewendet. Das heißt, wenn die Substitution des Template-Parameters fehlschlägt, wird die Spezialisierung aus der Menge aller möglichen Überladungen verworfen, aber dieser Fehler verursacht keinen Compilerfehler.std::conditional
ist der ternäre Operator zur Compiletime.std::common_type
bestimmt den gemeinsamen Typ aller Typen, in den alle Typen umgewandelt werden können.std::underlying_type
bestimmt den Typ einerenum
.
Vielleicht bist du noch nicht von den Vorteilen der Type-Traits-Bibliothek überzeugt. Ich möchte meine Beitragsserie daher mit ihren beiden Hauptzielen beenden: Korrektheit und Optimierung.
Korrektheit
Korrektheit bedeutet, dass du die Type-Traits-Bibliothek in C++11 nutzen kannst, um deinen Algorithmus sicherer zu machen. Die folgende Implementierung des gcd-Algorithmus setzt voraus, dass der binäre Modulo-Operator für seine Argumente gültig ist.
// gcd2.cpp
#include <iostream>
#include <type_traits>
template<typename T>
T gcd(T a, T b) {
static_assert(std::is_integral<T>::value, "T should be an integral type!"); // (1)
if( b == 0 ){ return a; }
else{
return gcd(b, a % b);
}
}
int main() {
std::cout << gcd(100, 33) << '\n';
std::cout << gcd(3.5,4.0) << '\n';
std::cout << gcd("100","10") << '\n';
}
Die Fehlermeldung ist eindeutig:
![Die Type-Traits Bibliothek: Korrektheit](https://heise.cloudimg.io/width/696/q85.png-lossy-85.webp-lossy-85.foil1/_www-heise-de_/imgs/18/3/2/3/0/6/8/6/gcd2-f9a0b75ffee39602.png)
Der Compiler beschwert sich sofort, dass ein double
oder ein const char*
kein ganzzahliger Datentyp ist, da der static_assert
-Ausdruck in (1) zuschlägt.
Aber Korrektheit bedeutet, dass du die Type-Traits-Bibliotheken nutzen kannst, um Concepts wie Integral
, SignedIntegral
und UnsignedIntegral
in C++20 zu implementieren.
template <typename T>
concept Integral = std::is_integral<T>::value; // (1)
template <typename T>
concept SignedIntegral = Integral<T> && std::is_signed<T>::value; // (2)
template <typename T>
concept UnsignedIntegral = Integral<T> && !SignedIntegral<T>;
Das Concept Integral
verwendet direkt die type-traits-Funktionen std::is_integra
l (1) und das Concept SignedIntegral
die type-traits-Funktion std::is_signed
(2). Probieren wir es aus und verwenden wir das Concept Integral
direkt.
// gcdIntegral.cpp
#include <iostream>
#include <type_traits>
template <typename T>
concept Integral = std::is_integral<T>::value;
template <typename T>
concept SignedIntegral = Integral<T> && std::is_signed<T>::value;
template <typename T>
concept UnsignedIntegral = Integral<T> && !SignedIntegral<T>;
Integral auto gcd(Integral auto a, decltype(a) b) {
if( b == 0 ){ return a; }
else{
return gcd(b, a % b);
}
}
int main() {
std::cout << gcd(100, 33) << '\n';
std::cout << gcd(3.5,4.0) << '\n';
std::cout << gcd("100","10") << '\n';
}
Der gcd-Algorithmus ist nun einfacher zu lesen. Er setzt voraus, dass das erste Argument a
und sein Rückgabetyp ganzzahlige Datentypen sind. Um sicherzustellen, dass das zweite Argument b
den gleichen Typ hat wie das erste Argument a,
habe ich seinen Typ als decltype(a)
angegeben. Folglich sind diese Implementierung des gcd-Algorithmus und die vorherige in gcd2.cpp gleichwertig.
Jetzt ist die Fehlermeldung ausführlicher als die vorherige:
![Die Type-Traits Bibliothek: Korrektheit](https://heise.cloudimg.io/width/696/q85.png-lossy-85.webp-lossy-85.foil1/_www-heise-de_/imgs/18/3/2/3/0/6/8/6/gcdIntegral-b43dd4478e6de183.png)
Die Fehlermeldung des GCC ist nicht nur zu langatmig, sondern auch zu schwer zu lesen. Lass mich Clang im Compiler Explorer ausprobieren. Die Fehlermeldung über die fehlerhafte Verwendung von double
liest sich wie Prosa:
![Die Type-Traits Bibliothek: Korrektheit](https://heise.cloudimg.io/width/696/q85.png-lossy-85.webp-lossy-85.foil1/_www-heise-de_/imgs/18/3/2/3/0/6/8/6/gcdIntegralClang-de4d72a1d1c9078e.png)
Ehrlich gesagt glaube ich nicht, dass eine Fehlermeldung verständlicher formuliert sein könnte.
Schließlich wollte ich noch den neuesten Microsoft Visual Studio Compiler ausprobieren. Dieser Compiler unterstützt Concepts mit einer Ausnahme: der sogenannten Abbreviated Function Template Syntax. Du hast es vielleicht schon erraten. Ich habe die Abbreviated Function Template Syntax in meinem gcd-Algorithmus verwendet. Du kannst mehr über diese schöne Syntax in meinem vorherigen Beitrag lesen: C++20: Concepts - Syntactic Sugar [2].
Wie geht es weiter?
Du weißt natürlich, worüber ich in meinem nächsten Beitrag schreiben werde: die Performanz-Geschichte der Type-Traits-Bibliothek. ( [3])
URL dieses Artikels:
https://www.heise.de/-6292251
Links in diesem Artikel:
[1] https://www.heise.de/blog/Die-Type-Traits-Bibliothek-std-is-base-of-6283615.html
[2] https://heise.de/-4615468
[3] mailto:rainer@grimm-jaud.de
Copyright © 2021 Heise Medien