C++
Type Traits
Recherche…
Remarques
Les traits de type sont des constructions basées sur des modèles utilisées pour comparer et tester les propriétés de différents types au moment de la compilation. Ils peuvent être utilisés pour fournir une logique conditionnelle au moment de la compilation qui peut limiter ou étendre les fonctionnalités de votre code de manière spécifique. La bibliothèque de caractères de type a été introduite avec le standard c++11
qui fournit un certain nombre de fonctionnalités différentes. Il est également possible de créer vos propres modèles de comparaison de caractères.
Traits de type standard
L'en type_traits
tête type_traits
contient un ensemble de classes de modèles et de helpers pour transformer et vérifier les propriétés des types au moment de la compilation.
Ces traits sont généralement utilisés dans les modèles pour vérifier les erreurs des utilisateurs, prendre en charge la programmation générique et permettre des optimisations.
La plupart des caractères sont utilisés pour vérifier si un type remplit certains critères. Ceux-ci ont la forme suivante:
template <class T> struct is_foo;
Si la classe de modèle est instancié avec un type qui répond à certains critères foo
, puis is_foo<T>
hérite de std::integral_constant<bool,true>
(alias std::true_type
), sinon il hérite de std::integral_constant<bool,false>
(aka std::false_type
). Cela donne le trait aux membres suivants:
Les constantes
static constexpr bool value
true
si T
remplit les critères foo
, false
sinon
Les fonctions
operator bool
Renvoie la value
bool operator()
Renvoie la value
Les types
prénom | Définition |
---|---|
value_type | bool |
type | std::integral_constant<bool,value> |
Le trait peut ensuite être utilisé dans des constructions telles que static_assert
ou std::enable_if
. Un exemple avec 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
}
Il existe également divers traits qui transforment les types, tels que std::add_pointer
et std::underlying_type
std::add_pointer
std::underlying_type
. Ces traits exposent généralement un type
membre de type unique qui contient le type transformé. Par exemple, std::add_pointer<int>::type
est int*
.
Tapez les relations avec std :: is_same
La relation de type std::is_same<T, T>
est utilisée pour comparer deux types. Il évaluera comme booléen, true si les types sont les mêmes et faux si autrement.
par exemple
// 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";
La relation de type std::is_same
fonctionnera également indépendamment des typedefs. Ceci est en fait démontré dans le premier exemple en comparant int == int32_t
mais ce n'est pas tout à fait clair.
par exemple
// Prints true on all compilers.
typedef int MyType
std::cout << std::is_same<int, MyType>::value << "\n";
Utiliser std::is_same
pour avertir lorsque vous utilisez incorrectement une classe ou une fonction std::is_same
sur un modèle.
Lorsqu'il est associé à une affirmation statique, le modèle std::is_same
peut être un outil précieux pour std::is_same
utilisation correcte des classes et des fonctions std::is_same
modèles.
Par exemple, une fonction qui n'autorise que les entrées d'un int
et un choix de deux structures.
#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;
}
Traits de type fondamentaux
Il existe un certain nombre de caractères différents qui comparent des types plus généraux.
Est intégral:
Evalue comme vrai pour tous les types entiers 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.
Est Flottant Point:
Évalue comme vrai pour tous les types à virgule flottante. 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.
Est Enum:
Évalue comme vrai pour tous les types énumérés, y compris la 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.
Est le pointeur:
Évalue comme vrai pour tous les pointeurs.
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.
Est classe:
Evalue comme vrai pour toutes les classes et struct, à l'exception de la 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.
Propriétés du type
Les propriétés de type comparent les modificateurs pouvant être placés sur différentes variables. L'utilité de ces caractères de type n'est pas toujours évidente.
Remarque: L'exemple ci-dessous n'offre qu'une amélioration sur un compilateur non optimisé. C'est un simple exemple de preuve de concept, plutôt qu'un exemple complexe.
Par exemple, diviser rapidement par quatre.
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);
}
Est constante:
Cela sera considéré comme vrai lorsque le type est constant.
std::cout << std::is_const<const int>::value << "\n"; // Prints true.
std::cout << std::is_const<int>::value << "\n"; // Prints false.
Est volatile:
Cela sera évalué comme vrai lorsque le type est volatile.
std::cout << std::is_volatile<static volatile int>::value << "\n"; // Prints true.
std::cout << std::is_const<const int>::value << "\n"; // Prints false.
Est signé:
Cela sera considéré comme vrai pour tous les types signés.
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.
Est non signé:
Sera évalué comme vrai pour tous les types non signés.
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.