C++
Eigenschaften eingeben
Suche…
Bemerkungen
Typmerkmale sind Templatkonstrukte, mit denen die Eigenschaften verschiedener Typen zur Kompilierzeit verglichen und getestet werden. Sie können verwendet werden, um eine bedingte Logik zur Kompilierzeit bereitzustellen, die die Funktionalität Ihres Codes auf bestimmte Weise einschränken oder erweitern kann. Die Typmerkmalsbibliothek wurde mit dem Standard c++11
der eine Reihe verschiedener Funktionalitäten bietet. Es ist auch möglich, eigene Vorlagen für den Vergleich von Typenmerkmalen zu erstellen.
Standardmerkmale
Der type_traits
Header enthält eine Reihe von Vorlagenklassen und Helfern, mit denen die Eigenschaften von Typen type_traits
der Kompilierung transformiert und überprüft werden können.
Diese Merkmale werden normalerweise in Vorlagen verwendet, um auf Benutzerfehler zu prüfen, generische Programmierung zu unterstützen und Optimierungen zu ermöglichen.
Die meisten Typmerkmale werden verwendet, um zu überprüfen, ob ein Typ einige Kriterien erfüllt. Diese haben folgende Form:
template <class T> struct is_foo;
Wenn die Vorlagenklasse mit einem Typ instanziiert wird, der einige Kriterien foo
erfüllt, erbt is_foo<T>
von std::integral_constant<bool,true>
(aka std::true_type
), andernfalls erbt sie von std::integral_constant<bool,false>
(auch bekannt als std::false_type
). Dies gibt dem Merkmal die folgenden Mitglieder:
Konstanten
static constexpr bool value
true
wenn T
die Kriterien foo
erfüllt, andernfalls false
Funktionen
operator bool
Gibt den value
bool operator()
Gibt den value
Typen
Name | Definition |
---|---|
value_type | bool |
type | std::integral_constant<bool,value> |
Das Merkmal kann dann in Konstrukten wie static_assert
oder std::enable_if
. Ein Beispiel mit std::is_pointer
:
template <typename T>
void i_require_a_pointer (T t) {
static_assert(std::is_pointer<T>::value, "T must be a pointer type");
}
//Overload for when T is not a pointer type
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value>::type
does_something_special_with_pointer (T t) {
//Do something boring
}
//Overload for when T is a pointer type
template <typename T>
typename std::enable_if<std::is_pointer<T>::value>::type
does_something_special_with_pointer (T t) {
//Do something special
}
Es gibt auch verschiedene Merkmale, die Typen transformieren, z. B. std::add_pointer
und std::underlying_type
. Diese Merkmale stellen im Allgemeinen einen einzelnen type
bereit, der den transformierten Typ enthält. Beispiel: std::add_pointer<int>::type
ist int*
.
Geben Sie Relations mit std :: is_same ein
Die Beziehung std::is_same<T, T>
wird zum Vergleich zweier Typen verwendet. Es wird als boolean ausgewertet, true, wenn die Typen gleich sind, andernfalls false.
z.B
// Prints true on most x86 and x86_64 compilers.
std::cout << std::is_same<int, int32_t>::value << "\n";
// Prints false on all compilers.
std::cout << std::is_same<float, int>::value << "\n";
// Prints false on all compilers.
std::cout << std::is_same<unsigned int, int>::value << "\n";
Die std::is_same
funktioniert auch unabhängig von Typedefs. Dies wird im ersten Beispiel beim Vergleich von int == int32_t
demonstriert. Dies ist jedoch nicht ganz klar.
z.B
// Prints true on all compilers.
typedef int MyType
std::cout << std::is_same<int, MyType>::value << "\n";
Verwenden von std::is_same
zum std::is_same
, wenn eine Klasse oder Funktion mit Vorlagen nicht ordnungsgemäß verwendet wird.
In Kombination mit einer statischen std::is_same
kann die Vorlage std::is_same
ein wertvolles Werkzeug sein, um die ordnungsgemäße Verwendung von Klassen und Funktionen mit Vorlagen zu erzwingen.
ZB eine Funktion, die nur Eingaben von einem int
und die Wahl von zwei Strukturen erlaubt.
#include <type_traits>
struct foo {
int member;
// Other variables
};
struct bar {
char member;
};
template<typename T>
int AddStructMember(T var1, int var2) {
// If type T != foo || T != bar then show error message.
static_assert(std::is_same<T, foo>::value ||
std::is_same<T, bar>::value,
"This function does not support the specified type.");
return var1.member + var2;
}
Grundtypeigenschaften
Es gibt eine Reihe verschiedener Typmerkmale, die allgemeinere Typen vergleichen.
Ist Integral:
Wird für alle Integer-Typen int
, char
, long
, unsigned int
usw. als wahr unsigned int
.
std::cout << std::is_integral<int>::value << "\n"; // Prints true.
std::cout << std::is_integral<char>::value << "\n"; // Prints true.
std::cout << std::is_integral<float>::value << "\n"; // Prints false.
Ist Floating Point:
Wird für alle Gleitkommatypen als wahr ausgewertet. float
, double
, long double
usw.
std::cout << std::is_floating_point<float>::value << "\n"; // Prints true.
std::cout << std::is_floating_point<double>::value << "\n"; // Prints true.
std::cout << std::is_floating_point<char>::value << "\n"; // Prints false.
Ist Enum:
Wird für alle Aufzählungstypen, einschließlich der Aufzählungsklasse, als wahr enum class
.
enum fruit {apple, pair, banana};
enum class vegetable {carrot, spinach, leek};
std::cout << std::is_enum<fruit>::value << "\n"; // Prints true.
std::cout << std::is_enum<vegetable>::value << "\n"; // Prints true.
std::cout << std::is_enum<int>::value << "\n"; // Prints false.
Ist Zeiger:
Bewertet für alle Zeiger als wahr.
std::cout << std::is_pointer<int *>::value << "\n"; // Prints true.
typedef int* MyPTR;
std::cout << std::is_pointer<MyPTR>::value << "\n"; // Prints true.
std::cout << std::is_pointer<int>::value << "\n"; // Prints false.
Ist Klasse:
Wird für alle Klassen und struct mit Ausnahme von enum class
als wahr enum class
.
struct FOO {int x, y;};
class BAR {
public:
int x, y;
};
enum class fruit {apple, pair, banana};
std::cout << std::is_class<FOO>::value << "\n"; // Prints true.
std::cout << std::is_class<BAR>::value << "\n"; // Prints true.
std::cout << std::is_class<fruit>::value << "\n"; // Prints false.
std::cout << std::is_class<int>::value << "\n"; // Prints false.
Geben Sie Eigenschaften ein
Typeneigenschaften vergleichen die Modifizierer, die für verschiedene Variablen platziert werden können. Die Nützlichkeit dieser Merkmale ist nicht immer offensichtlich.
Hinweis: Das folgende Beispiel bietet nur eine Verbesserung eines nicht optimierenden Compilers. Es ist eher ein Beweis für das Konzept als ein komplexes Beispiel.
zB schnelle Division durch vier.
template<typename T>
inline T FastDivideByFour(cont T &var) {
// Will give an error if the inputted type is not an unsigned integral type.
static_assert(std::is_unsigned<T>::value && std::is_integral<T>::value,
"This function is only designed for unsigned integral types.");
return (var >> 2);
}
Ist konstant:
Dies wird als wahr ausgewertet, wenn type konstant ist.
std::cout << std::is_const<const int>::value << "\n"; // Prints true.
std::cout << std::is_const<int>::value << "\n"; // Prints false.
Ist flüchtig:
Dies wird als wahr ausgewertet, wenn der Typ flüchtig ist.
std::cout << std::is_volatile<static volatile int>::value << "\n"; // Prints true.
std::cout << std::is_const<const int>::value << "\n"; // Prints false.
Ist unterschrieben:
Dies wird für alle signierten Typen als wahr bewertet.
std::cout << std::is_signed<int>::value << "\n"; // Prints true.
std::cout << std::is_signed<float>::value << "\n"; // Prints true.
std::cout << std::is_signed<unsigned int>::value << "\n"; // Prints false.
std::cout << std::is_signed<uint8_t>::value << "\n"; // Prints false.
Ist unsigniert:
Wird für alle vorzeichenlosen Typen als wahr ausgewertet.
std::cout << std::is_unsigned<unsigned int>::value << "\n"; // Prints true.
std::cout << std::is_signed<uint8_t>::value << "\n"; // Prints true.
std::cout << std::is_unsigned<int>::value << "\n"; // Prints false.
std::cout << std::is_signed<float>::value << "\n"; // Prints false.