Experimenté un comportamiento extraño mientras usaba rasgos de tipo C ++ y he reducido mi problema a este pequeño y peculiar problema para el que daré un montón de explicaciones, ya que no quiero dejar nada abierto a una mala interpretación.
Digamos que tiene un programa como este:
#include <iostream>
#include <cstdint>
template <typename T>
bool is_int64() { return false; }
template <>
bool is_int64<int64_t>() { return true; }
int main()
{
std::cout << "int:\t" << is_int64<int>() << std::endl;
std::cout << "int64_t:\t" << is_int64<int64_t>() << std::endl;
std::cout << "long int:\t" << is_int64<long int>() << std::endl;
std::cout << "long long int:\t" << is_int64<long long int>() << std::endl;
return 0;
}
En la compilación de 32 bits con GCC (y con MSVC de 32 y 64 bits), la salida del programa será:
int: 0
int64_t: 1
long int: 0
long long int: 1
Sin embargo, el programa resultante de una compilación GCC de 64 bits generará:
int: 0
int64_t: 1
long int: 1
long long int: 0
Esto es curioso, ya que long long int
es un entero de 64 bits con signo y es, para todos los efectos, idéntico a los tipos long int
y int64_t
, por lo tanto, lógicamente int64_t
, long int
y long long int
serían tipos equivalentes: el ensamblado generado al usar estos tipos es idéntico. Una mirada stdint.h
me dice por qué:
# if __WORDSIZE == 64
typedef long int int64_t;
# else
__extension__
typedef long long int int64_t;
# endif
En una compilación de 64 bits, int64_t
es long int
, no una long long int
(obviamente).
La solución para esta situación es bastante fácil:
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
Pero esto es horriblemente pirateado y no se escala bien (funciones reales de sustancia uint64_t
, etc.). Entonces mi pregunta es: ¿Hay alguna manera de decirle al compilador que a long long int
es también a int64_t
, como long int
es?
Mis pensamientos iniciales son que esto no es posible, debido a la forma en que funcionan las definiciones de tipo C / C ++. No hay manera de especificar la equivalencia de tipos de los tipos de datos básicos para el compilador, ya que ese es el trabajo del compilador (y permitir eso podría romper muchas cosas) y typedef
solo va en una dirección.
Tampoco estoy muy preocupado por obtener una respuesta aquí, ya que este es un caso de borde súper duper que no sospecho que a nadie le importará cuando los ejemplos no estén horriblemente ideados (¿eso significa que esto debería ser un wiki de la comunidad?) .
Agregar : La razón por la que estoy usando la especialización de plantilla parcial en lugar de un ejemplo más fácil como:
void go(int64_t) { }
int main()
{
long long int x = 2;
go(x);
return 0;
}
es que dicho ejemplo aún se compilará, ya que long long int
es implícitamente convertible a un int64_t
.
Agregar : la única respuesta hasta ahora asume que quiero saber si un tipo es de 64 bits. No quería engañar a la gente para que pensara que eso me importa y que probablemente debería haber proporcionado más ejemplos de dónde se manifiesta este problema.
template <typename T>
struct some_type_trait : boost::false_type { };
template <>
struct some_type_trait<int64_t> : boost::true_type { };
En este ejemplo, some_type_trait<long int>
será un boost::true_type
, pero some_type_trait<long long int>
no lo será. Si bien esto tiene sentido en la idea de tipos de C ++, no es deseable.
Otro ejemplo es usar un calificador como same_type
(que es bastante común de usar en C ++ 0x Concepts):
template <typename T>
void same_type(T, T) { }
void foo()
{
long int x;
long long int y;
same_type(x, y);
}
Ese ejemplo no se compila, ya que C ++ (correctamente) ve que los tipos son diferentes. g ++ fallará al compilar con un error como: no hay llamada de función coincidente same_type(long int&, long long int&)
.
Me gustaría enfatizar que entiendo por qué sucede esto, pero estoy buscando una solución que no me obligue a repetir el código por todas partes.
sizeof
cada tipo? Quizás el compilador esté tratando el tamaño de de maneralong long int
diferente.<cstdint>
, así que quizás el hecho de que tenga que decir "esto es una extensión" (que lo es) lo esté engañando.--std=c++0x
. Y sí,sizeof(long long int) == sizeof(long int) == sizeof(int64_t) == 8
.long
ylong long
son tipos distintos (incluso si tienen el mismo tamaño y representación).int64_t
es siempre un alias para otro tipo existente (a pesar de su nombre,typedef
no crea nuevos tipos, solo le da un alias a uno que ya existe)int16_t
, entonces especialícese conshort
yint
y estará cubierto. (y consigned char
si te sientes aventurero)Respuestas:
No es necesario ir a 64 bits para ver algo como esto. Piense
int32_t
en plataformas comunes de 32 bits. Puede sertypedef
editado comoint
o comolong
, pero obviamente solo uno de los dos a la vez.int
ylong
, por supuesto, son tipos distintos.No es difícil ver que no existe una solución alternativa
int == int32_t == long
para los sistemas de 32 bits. Por la misma razón, no hay forma de hacerlolong == int64_t == long long
en sistemas de 64 bits.Si pudiera, las posibles consecuencias serían bastante doloroso para el código que sobrecargado
foo(int)
,foo(long)
yfoo(long long)
- de repente tendrían dos definiciones para la misma sobrecarga ?!La solución correcta es que el código de su plantilla generalmente no debería depender de un tipo preciso, sino de las propiedades de ese tipo. Toda la
same_type
lógica aún podría estar bien para casos específicos:long foo(long x); std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);
Es decir, la sobrecarga
foo(int64_t)
no se define cuando es exactamente igual quefoo(long)
.[editar] Con C ++ 11, ahora tenemos una forma estándar de escribir esto:
long foo(long x); std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);
[editar] O C ++ 20
long foo(long x); int64_t foo(int64_t) requires (!std::is_same_v<int64_t, long>);
fuente
sizeof()
long
yint
es idéntica, perostd::is_same<long, int>::value
regresafalse
. La misma rareza en AppleClang 9.1 en OSX HighSierra.¿Quieres saber si un tipo es del mismo tipo que int64_t o quieres saber si algo es de 64 bits? Según su solución propuesta, creo que está preguntando sobre este último. En ese caso, haría algo como
template<typename T> bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64
fuente
return
y un punto y coma?sizeof
para esto.short
= 16 bits,long
= 32 bits yint
= tamaño nativo. En estos días de 64 bits,int
yalong
no significan nada.short
tiene al menos 16 bits,int
tiene al menos 16 bits ylong
al menos 32 bits, con (sigue una notación descuidada) corto <= int <= largo. Los "viejos tiempos" a los que te refieres nunca existieron; siempre ha habido variaciones dentro de las restricciones impuestas por el idioma. La falacia de "Todo el mundo es un x86" es tan peligrosa como la antigua "Todo el mundo es una falacia de VAX."Esta es una buena pregunta o problema, pero sospecho que la respuesta es NO.
Además, a
long int
puede no ser along long int
.Creo que esto es libc. Sospecho que quieres profundizar más.
Linux de 32 bits utiliza el modelo de datos ILP32. Los números enteros, largos y punteros son de 32 bits. El tipo de 64 bits es un
long long
.Microsoft documenta los rangos en rangos de tipos de datos . Dicen que
long long
es equivalente a__int64
.Linux de 64 bits usa el
LP64
modelo de datos. Los largos son de 64 bits y delong long
64 bits. Al igual que con 32 bits, Microsoft documenta los rangos en los rangos de tipos de datos y long long is still__int64
.Hay un
ILP64
modelo de datos donde todo es de 64 bits. Tienes que hacer un trabajo adicional para obtener una definición para tuword32
tipo. Consulte también artículos como Modelos de programación de 64 bits: ¿Por qué LP64?Sí, se pone aún mejor. GCC mezcla y combina declaraciones que se supone deben tomar tipos de 64 bits, por lo que es fácil meterse en problemas aunque siga un modelo de datos en particular. Por ejemplo, lo siguiente provoca un error de compilación y le indica que utilice
-fpermissive
:#if __LP64__ typedef unsigned long word64; #else typedef unsigned long long word64; #endif // intel definition of rdrand64_step (http://software.intel.com/en-us/node/523864) // extern int _rdrand64_step(unsigned __int64 *random_val); // Try it: word64 val; int res = rdrand64_step(&val);
En resultado de:
error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'
Entonces, ignórelo
LP64
y cámbielo a:typedef unsigned long long word64;
Luego, diríjase a un dispositivo ARM IoT de 64 bits que define
LP64
y usa NEON:error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'
fuente