C++
Tipo de rasgos
Buscar..
Observaciones
Los rasgos de tipo son construcciones con plantillas que se utilizan para comparar y probar las propiedades de diferentes tipos en el momento de la compilación. Se pueden usar para proporcionar una lógica condicional en el momento de la compilación que puede limitar o ampliar la funcionalidad de su código de una manera específica. La biblioteca de rasgos de tipo se incorporó con el estándar c++11
, que proporciona una serie de funcionalidades diferentes. También es posible crear sus propias plantillas de comparación de rasgos de tipo.
Rasgos de tipo estándar
El encabezado type_traits
contiene un conjunto de clases de plantilla y ayudantes para transformar y verificar las propiedades de los tipos en tiempo de compilación.
Estos rasgos se usan normalmente en las plantillas para verificar errores de los usuarios, admitir la programación genérica y permitir optimizaciones.
La mayoría de los rasgos de tipo se utilizan para verificar si un tipo cumple con algunos criterios. Estos tienen la siguiente forma:
template <class T> struct is_foo;
Si la clase de plantilla está instanciada con un tipo que cumple con algunos criterios foo
, is_foo<T>
hereda de std::integral_constant<bool,true>
(también conocido como std::true_type
), de lo contrario, hereda de std::integral_constant<bool,false>
(también conocido como std::false_type
). Esto le da al rasgo los siguientes miembros:
Constantes
static constexpr bool value
true
si T
cumple con los criterios foo
, false
contrario
Funciones
operator bool
Devuelve value
bool operator()
Devuelve value
Los tipos
Nombre | Definición |
---|---|
value_type | bool |
type | std::integral_constant<bool,value> |
El rasgo se puede usar en construcciones como static_assert
o std::enable_if
. Un ejemplo 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
}
También hay varios rasgos que transforman los tipos, como std::add_pointer
y std::underlying_type
. Estos rasgos generalmente exponen un type
miembro de tipo único que contiene el tipo transformado. Por ejemplo, std::add_pointer<int>::type
is int*
.
Escribe relaciones con std :: is_same
La relación de tipo std::is_same<T, T>
se utiliza para comparar dos tipos. Se evaluará como booleano, verdadero si los tipos son los mismos y falso en caso contrario.
p.ej
// 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 relación de tipo std::is_same
también funcionará independientemente de typedefs. Esto se demuestra realmente en el primer ejemplo cuando se compara int == int32_t
sin embargo, esto no está del todo claro.
p.ej
// Prints true on all compilers.
typedef int MyType
std::cout << std::is_same<int, MyType>::value << "\n";
Usar std::is_same
para advertir cuando se usa incorrectamente una clase o función con plantilla.
Cuando se combina con una std::is_same
estática, la plantilla std::is_same
puede ser una herramienta valiosa para imponer el uso adecuado de las clases y funciones de plantilla.
por ejemplo, una función que solo permite la entrada desde un int
y una opción de dos estructuras.
#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;
}
Rasgos fundamentales de tipo
Hay una serie de rasgos de tipos diferentes que comparan tipos más generales.
Es integral:
Evalúa como verdadero para todos los tipos de enteros 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.
Es punto flotante:
Evalúa como verdadero para todos los tipos de punto flotante. 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.
Es enum:
Evalúa como verdadero para todos los tipos enumerados, incluida 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.
Es puntero:
Evalúa como verdadero para todos los punteros.
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.
Es la clase:
Se evalúa como verdadero para todas las clases y estructuras, con la excepción 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.
Tipo de propiedades
Las propiedades de tipo comparan los modificadores que se pueden colocar sobre diferentes variables. La utilidad de estos rasgos de tipo no siempre es obvia.
Nota: El ejemplo a continuación solo ofrecería una mejora en un compilador que no optimiza. Es una simple prueba de concepto, en lugar de un ejemplo complejo.
Por ejemplo, dividir rápido por cuatro.
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);
}
Es constante:
Esto se evaluará como verdadero cuando el tipo es constante.
std::cout << std::is_const<const int>::value << "\n"; // Prints true.
std::cout << std::is_const<int>::value << "\n"; // Prints false.
Es volátil:
Esto se evaluará como verdadero cuando el tipo es volátil.
std::cout << std::is_volatile<static volatile int>::value << "\n"; // Prints true.
std::cout << std::is_const<const int>::value << "\n"; // Prints false.
Está firmado:
Esto se evaluará como verdadero para todos los tipos firmados.
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á sin firmar:
Se evaluará como verdadero para todos los tipos sin firmar.
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.