Pregunta

Me experimentaron algún comportamiento extraño durante el uso de C ++ rasgos de tipo y han estrechado mi problema a este pequeño problema peculiar de la cual yo daré una tonelada de explicación ya que no quiero dejar nada abierto a interpretaciones erróneas.

Supongamos que tiene un programa de este modo:

#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 tanto compilación de 32 bits con GCC (y con 32 y 64 bits MSVC), la salida del programa será:

int:           0
int64_t:       1
long int:      0
long long int: 1

Sin embargo, el programa resultante de una salida de GCC de compilación voluntad de 64 bits:

int:           0
int64_t:       1
long int:      1
long long int: 0

Esto es curioso, ya 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 que lógicamente, int64_t, long int y long long int serían tipos equivalentes - el conjunto generado cuando el uso de estos tipos es idéntico. Una mirada a 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 un long long int (obviamente).

La solución a 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 terriblemente hacker y no escala bien (las funciones reales de la sustancia, uint64_t, etc). Así que mi pregunta es:? ¿Hay una manera de decirle al compilador que una long long int es la int64_t también, al igual que long int es


Mis pensamientos iniciales son que esto no es posible, debido a la forma en C / C ++ definiciones de tipos de trabajo. No hay una forma de especificar el tipo de equivalencia de los tipos de datos básicos para el compilador, ya que ese es el trabajo del compilador (y permitiendo que podría romper un montón de cosas) y typedef en un solo sentido.

También soy no demasiado preocupado por conseguir una respuesta aquí, ya que este es un caso requete borde que no sospechan alguien alguna vez se preocupan cuando los ejemplos no son artificiales horrible (¿significa que esta debe ser la comunidad wiki?).


Anexar : La razón por la que estoy usando 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 todavía compilar, ya long long int es implícitamente convertible a un int64_t.


Anexar : la única respuesta hasta ahora supone que yo quiero saber si un tipo es de 64-bits. No quería que la gente erróneamente a pensar que me importa eso y probablemente debería haber proporcionado más ejemplos en los que este problema se manifiesta.

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á. Mientras que esta idea tiene sentido en C ++ 's de tipos, no es deseable.

Otro ejemplo es utilizar un calificador como same_type (que es bastante común para uso en C ++ 0x Conceptos):

template <typename T>
void same_type(T, T) { }

void foo()
{
    long int x;
    long long int y;
    same_type(x, y);
}

Ese ejemplo falla al compilar, ya que C ++ (correctamente) ve que los tipos son diferentes. g ++ dejará de compilar con un error como:. no same_type(long int&, long long int&) llamada de función a juego

Me gustaría hacer hincapié en que entiendo ¿Por qué esto está sucediendo, pero estoy en busca de una solución que no me obliga a repetir código de todo el lugar.

¿Fue útil?

Solución

Usted no necesita ir a 64 bits para ver algo como esto. Considere int32_t en plataformas comunes de 32 bits. Se podría typedef'ed como int o como long, pero sólo uno de los dos, obviamente, a la vez. int y long son de tipos distintos de golf.

No es difícil ver que no hay ninguna solución que hace int == int32_t == long en sistemas de 32 bits. Por la misma razón, no hay manera de hacer long == int64_t == long long en sistemas de 64 bits.

Si pudiera, las posibles consecuencias sería bastante doloroso para el código que sobrecargado foo(int), foo(long) y foo(long long) - pronto tendrían dos definiciones para el mismo sobrecarga

La solución correcta es que su código de la plantilla por lo general no debe ser un partido basado en un tipo preciso, pero en las propiedades de ese tipo. La lógica same_type conjunto todavía 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 foo(int64_t) sobrecarga no está definido cuando es exactamente lo mismo que foo(long).

[editar] Con C ++ 11, ahora tenemos una manera 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);

Otros consejos

¿Quieres saber si un tipo es del mismo tipo que int64_t o quiere saber si algo es de 64 bits? Sobre la base de la solución propuesta, creo que estás preguntando por este último. En ese caso, me gustaría hacer algo como

template<typename T>
bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64

Así que mi pregunta es: ¿Hay una manera de decirle al compilador que un largo tiempo es la int también un int64_t, al igual que a largo int es

Esta es una buena pregunta o problema, pero sospecho que la respuesta es NO.

Además, un long int puede no ser una long long int.


# if __WORDSIZE == 64
typedef long int  int64_t;
# else
__extension__
typedef long long int  int64_t;
# endif

creo que esto es libc. Sospecho que quiere ir más profundo.

En tanto compilación de 32 bits con GCC (y con 32 y 64 bits MSVC), el salida del programa será:

int:           0
int64_t:       1
long int:      0
long long int: 1

Linux de 32 bits utiliza el modelo de datos ILP32. Números enteros, largos y punteros son de 32 bits. El tipo de 64 bits es un long long.

documentos de Microsoft en los rangos Tipo Rangos de datos . El dicen que el long long es equivalente a __int64.

Sin embargo, el programa resultante de una salida de GCC de compilación voluntad de 64 bits:

int:           0
int64_t:       1
long int:      1
long long int: 0

Linux de 64 bits utiliza el modelo de datos LP64. Longs son de 64 bits y long long son de 64 bits. Al igual que con 32 bits, documentos de Microsoft en los rangos Tipo de datos Rangos y mucho, mucho sigue siendo __int64.

Hay un modelo de datos ILP64 donde todo es de 64 bits. Usted tiene que hacer un trabajo extra para conseguir una definición para su tipo word32. Ver también papeles como 64 bits Modelos de Programación: ¿Por qué LP64

?

Pero esto es terriblemente hacker y no escala bien (las funciones reales de la sustancia, uint64_t, etc) ...

Sí, se pone aún mejor. mezclas de GCC y partidos declaraciones que se supone que tienen 64 tipos de bits, así que es fácil de conseguir en problemas a pesar de que se sigue un modelo de datos en particular. Por ejemplo, la siguiente provoca un error de compilación y le dice a su uso -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);

El resultado es:

error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'

Por lo tanto, ignorar LP64 y cambiarlo a:

typedef unsigned long long word64;

A continuación, pasear a un 64-bit herramienta de ARM IO que define LP64 y el uso NEON:

error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top