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.