zurück zum Artikel

Die Type-Traits-Bibliothek: Korrektheit

Rainer Grimm

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

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.

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:

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 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

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_integral (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

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

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].

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