C++
Typ eigenschappen
Zoeken…
Opmerkingen
Typekenmerken zijn sjabloonconstructies die worden gebruikt om de eigenschappen van verschillende typen tijdens het compileren te vergelijken en te testen. Ze kunnen worden gebruikt om tijdens het compileren voorwaardelijke logica te bieden die de functionaliteit van uw code op een specifieke manier kan beperken of uitbreiden. De bibliotheek met typekenmerken is ingevoerd met de standaard c++11
die een aantal verschillende functionaliteiten biedt. Het is ook mogelijk om uw eigen type vergelijkingstekens voor typen te maken.
Standaard type eigenschappen
De header type_traits
bevat een set sjabloonklassen en helpers om eigenschappen van typen tijdens het compileren te transformeren en te controleren.
Deze eigenschappen worden meestal in sjablonen gebruikt om te controleren op gebruikersfouten, ondersteunen generieke programmering en zorgen voor optimalisaties.
De meeste typekenmerken worden gebruikt om te controleren of een type aan enkele criteria voldoet. Deze hebben de volgende vorm:
template <class T> struct is_foo;
Als de sjabloonklasse wordt geïnstantieerd met een type dat aan een aantal criteria foo
voldoet, neemt is_foo<T>
van std::integral_constant<bool,true>
(aka std::true_type
), anders neemt het over van std::integral_constant<bool,false>
(aka std::false_type
). Dit geeft de eigenschap de volgende leden:
constanten
static constexpr bool value
true
als T
voldoet aan de criteria foo
, anders false
functies
operator bool
Retourneert value
bool operator()
Retourneert value
Types
Naam | Definitie |
---|---|
value_type | bool |
type | std::integral_constant<bool,value> |
De eigenschap kan vervolgens worden gebruikt in constructies zoals static_assert
of std::enable_if
. Een voorbeeld met 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
}
Er zijn ook verschillende eigenschappen die typen transformeren, zoals std::add_pointer
en std::underlying_type
std::add_pointer
. Deze eigenschappen onthullen over het algemeen een enkel type
lidtype dat het getransformeerde type bevat. Bijvoorbeeld: std::add_pointer<int>::type
is int*
.
Typ relaties met std :: is_same
De relatie type std::is_same<T, T>
wordt gebruikt om twee typen te vergelijken. Het zal als Boolean evalueren, waar als de typen hetzelfde zijn en onwaar als anders.
bv
// 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";
De relatie type std::is_same
werkt ook ongeacht typedefs. Dit wordt in het eerste voorbeeld aangetoond bij het vergelijken van int == int32_t
maar dit is niet helemaal duidelijk.
bv
// Prints true on all compilers.
typedef int MyType
std::cout << std::is_same<int, MyType>::value << "\n";
Het gebruik van std::is_same
om te waarschuwen bij het onjuist gebruiken van een templated klasse of functie.
In combinatie met een statische bewering kan de sjabloon std::is_same
een waardevol hulpmiddel zijn bij het afdwingen van correct gebruik van templated klassen en functies.
bijv. Een functie die alleen invoer toestaat van een int
en een keuze uit twee structs.
#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;
}
Fundamentele karaktertrekken
Er zijn een aantal verschillende typekenmerken die meer algemene typen vergelijken.
Is integraal:
Evalueert als waar voor alle typen gehele getallen int
, char
, long
, unsigned int
etc.
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.
Is drijvend punt:
Evalueert als waar voor alle drijvende komma types. float
, double
, long double
etc.
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.
Is Enum:
Evalueert als waar voor alle genoemde typen, inclusief 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.
Is aanwijzer:
Evalueert als waar voor alle wijzers.
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.
Is klasse:
Evalueert als waar voor alle klassen en struct, met uitzondering van 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.
Type eigenschappen
Type-eigenschappen vergelijken de modificatoren die op verschillende variabelen kunnen worden geplaatst. Het nut van deze typekenmerken is niet altijd duidelijk.
Opmerking: het onderstaande voorbeeld biedt alleen een verbetering op een niet-optimaliserende compiler. Het is een eenvoudig proof of concept, in plaats van een complex voorbeeld.
bijv. Snel delen door 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);
}
Is constant:
Dit wordt als waar geëvalueerd wanneer het type constant is.
std::cout << std::is_const<const int>::value << "\n"; // Prints true.
std::cout << std::is_const<int>::value << "\n"; // Prints false.
Is vluchtig:
Dit wordt als waar geëvalueerd wanneer het type vluchtig is.
std::cout << std::is_volatile<static volatile int>::value << "\n"; // Prints true.
std::cout << std::is_const<const int>::value << "\n"; // Prints false.
Is ondertekend:
Dit wordt als waar geëvalueerd voor alle ondertekende typen.
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.
Is niet ondertekend:
Zal evalueren als waar voor alle niet-ondertekende typen.
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.