C++
Tipo Tratti
Ricerca…
Osservazioni
I tratti di tipo sono costrutti basati su modelli utilizzati per confrontare e testare le proprietà di diversi tipi in fase di compilazione. Possono essere utilizzati per fornire una logica condizionale in fase di compilazione che può limitare o estendere la funzionalità del codice in un modo specifico. La libreria dei caratteri di tipo è stata introdotta con lo standard c++11
che fornisce un numero di funzionalità diverse. È anche possibile creare modelli di confronto trait personalizzati.
Tratti di tipo standard
L'intestazione type_traits
contiene un insieme di classi template e helper per trasformare e controllare le proprietà dei tipi in fase di compilazione.
Questi tratti sono in genere utilizzati nei modelli per verificare gli errori degli utenti, supportare la programmazione generica e consentire ottimizzazioni.
La maggior parte dei caratteri tipografici viene utilizzata per verificare se un tipo soddisfa alcuni criteri. Questi hanno la seguente forma:
template <class T> struct is_foo;
Se la classe template è istanziata con un tipo che soddisfa alcuni criteri foo
, allora is_foo<T>
eredita da std::integral_constant<bool,true>
(aka std::true_type
), altrimenti eredita da std::integral_constant<bool,false>
(aka std::false_type
). Questo conferisce al tratto i seguenti membri:
costanti
static constexpr bool value
true
se T
soddisfa i criteri foo
, false
altrimenti
funzioni
operator bool
Restituisce value
bool operator()
Restituisce value
tipi
Nome | Definizione |
---|---|
value_type | bool |
type | std::integral_constant<bool,value> |
Il tratto può quindi essere utilizzato in costrutti come static_assert
o std::enable_if
. Un esempio con 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
}
Ci sono anche vari tratti che trasformano i tipi, come std::add_pointer
e std::underlying_type
. Questi tratti generalmente espongono un type
membro di tipo singolo che contiene il tipo trasformato. Ad esempio, std::add_pointer<int>::type
è int*
.
Digitare le relazioni con std :: is_same
La relazione di tipo std::is_same<T, T>
viene utilizzata per confrontare due tipi. Valuterà come booleano, vero se i tipi sono uguali e falso se diversamente.
per esempio
// 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";
Anche la relazione di tipo std::is_same
funzionerà indipendentemente da typedef. Questo è effettivamente dimostrato nel primo esempio quando si confronta int == int32_t
tuttavia questo non è completamente chiaro.
per esempio
// Prints true on all compilers.
typedef int MyType
std::cout << std::is_same<int, MyType>::value << "\n";
Usando std::is_same
per avvisare quando si utilizza impropriamente una classe o una funzione std::is_same
su modello.
Quando combinato con un std::is_same
statico, il modello std::is_same
può essere uno strumento prezioso per far rispettare l'uso corretto di classi e funzioni std::is_same
su modelli.
Ad esempio una funzione che consente solo l'input da un int
e una scelta di due strutture.
#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;
}
Caratteri fondamentali del tipo
Ci sono un certo numero di tratti di tipo diverso che confrontano tipi più generali.
È integrale:
Valuta come vero per tutti i tipi interi int
, char
, long
, unsigned int
ecc.
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.
È in virgola mobile:
Valuta come vero per tutti i tipi di virgola mobile. float
, double
, long double
ecc.
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.
È Enum:
Valuta come vero per tutti i tipi enumerati, inclusa 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.
È puntatore:
Valuta come vero per tutti i puntatori.
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.
È classe:
Valuta come vero per tutte le classi e struct, ad eccezione della 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.
Tipo Proprietà
Le proprietà del tipo confrontano i modificatori che possono essere posizionati su variabili diverse. L'utilità di questi tratti del genere non è sempre ovvia.
Nota: l'esempio seguente offre solo un miglioramento su un compilatore non ottimizzante. È una semplice dimostrazione di concetto, piuttosto che un esempio complesso.
es. Divisione veloce per quattro.
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);
}
È costante:
Questo valuterà come vero quando il tipo è costante.
std::cout << std::is_const<const int>::value << "\n"; // Prints true.
std::cout << std::is_const<int>::value << "\n"; // Prints false.
È volatile:
Questo verrà valutato come true quando il tipo è volatile.
std::cout << std::is_volatile<static volatile int>::value << "\n"; // Prints true.
std::cout << std::is_const<const int>::value << "\n"; // Prints false.
È firmato:
Questo valuterà come vero per tutti i tipi firmati.
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.
Non firmato:
Valuterà come vero per tutti i tipi non firmati.
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.