long long int vs long int vs int64_t en C ++
Question
Je connu un comportement étrange en utilisant des traits de type C ++ et ont réduit mon problème à ce petit problème bizarre pour lequel je donnerai une tonne d'explication car je ne veux pas laisser quoi que ce soit ouvert à une interprétation erronée.
Disons que vous avez un programme comme ceci:
#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;
}
Dans les deux 32 bits de la compilation avec GCC (et 32 ??et 64 bits MSVC), la sortie du programme sera:
int: 0
int64_t: 1
long int: 0
long long int: 1
Toutefois, le programme résultant d'une sortie de la volonté de compilation GCC 64 bits:
int: 0
int64_t: 1
long int: 1
long long int: 0
Ceci est curieux, puisque long long int
est un entier signé 64 bits et est, à toutes fins utiles, identiques aux types de long int
et int64_t
, donc logiquement, int64_t
, long int
et long long int
seraient types équivalents - l'ensemble généré lorsque l'utilisation de ces types est identique. Un coup d'oeil à stdint.h
me dit pourquoi:
# if __WORDSIZE == 64
typedef long int int64_t;
# else
__extension__
typedef long long int int64_t;
# endif
Dans une compilation de 64 bits, int64_t
est long int
, pas un long long int
(évidemment).
Le correctif de cette situation est assez facile:
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
Mais est horriblement hackish et n'échelle pas bien (fonctions réelles de fond, uint64_t
, etc.). Ma question est: Est-il possible de dire au compilateur qu'un long long int
est aussi un int64_t
, tout comme long int
est
Mes premières impressions sont que cela est impossible, en raison de la manière C / C ++ de travail des définitions de type. Il n'y a pas un moyen de spécifier l'équivalence de type des types de données de base au compilateur, puisque c'est le travail du compilateur (et permettant cela pourrait briser beaucoup de choses) et typedef
est à sens unique.
Je suis aussi pas trop préoccupé à obtenir une réponse ici, puisque c'est un cas super-Duper bord que je ne quelqu'un suspect jamais se soucier quand les exemples ne sont pas arrangeaient horriblement (que cela signifie cela devrait être la communauté wiki?).
Append : La raison pour laquelle j'utilise la spécialisation de modèle partiel au lieu d'un exemple plus facile comme:
void go(int64_t) { }
int main()
{
long long int x = 2;
go(x);
return 0;
}
est que ledit exemple va encore compiler, puisque long long int
est implicitement convertible en une int64_t
.
Append : La seule réponse si loin suppose que je veux savoir si un type est de 64 bits. Je ne voulais pas les induire en erreur en pensant que je garde à ce sujet et devrait probablement avoir fourni plus des exemples où ce problème se manifeste.
template <typename T>
struct some_type_trait : boost::false_type { };
template <>
struct some_type_trait<int64_t> : boost::true_type { };
Dans cet exemple, some_type_trait<long int>
sera un boost::true_type
, mais some_type_trait<long long int>
ne sera pas. Même si cela est logique dans idée des types de C ++, il est souhaitable.
Un autre exemple utilise un qualificatif comme same_type
(ce qui est assez courant d'utiliser 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);
}
Cet exemple ne peut pas compiler, puisque C ++ (correctement) voit que les types sont différents. g ++ ne parviendra pas à compiler avec une erreur comme:. same_type(long int&, long long int&)
pas d'appel de fonction correspondant
Je voudrais souligner que je comprends pourquoi ce qui se passe, mais je suis à la recherche d'une solution de contournement qui ne me force pas à répéter le code dans tous les sens.
La solution
Vous n'avez pas besoin d'aller à 64 bits pour voir quelque chose comme ça. Tenez compte int32_t
sur des plateformes communes 32 bits. Il pourrait être typedef
'ed comme int
ou comme long
, mais il est évident que l'un des deux à la fois. int
et long
sont des types bien sûr distincts.
Il est pas difficile de voir qu'il n'y a pas de solution qui fait int == int32_t == long
sur les systèmes 32 bits. Pour la même raison, il n'y a pas moyen de faire long == int64_t == long long
sur les systèmes 64 bits.
Si vous pouviez, les conséquences possibles serait assez pénible pour le code qui foo(int)
surchargé, foo(long)
et foo(long long)
-! Tout à coup ils auraient deux définitions pour la même surcharge
La bonne solution est que généralement votre code modèle doit ne se fonderait sur un type précis, mais sur les propriétés de ce type. Toute la logique de same_type
pourrait encore être OK pour les cas spécifiques:
long foo(long x);
std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);
i.e.., La foo(int64_t)
de surcharge ne se définit pas quand elle est en exactement la même chose que foo(long)
.
[modifier] Avec C ++ 11, nous avons maintenant un moyen standard pour écrire ceci:
long foo(long x);
std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);
Autres conseils
Est-ce que vous voulez savoir si un type est du même type que int64_t ou voulez-vous savoir si quelque chose est de 64 bits? Sur la base de la solution proposée, je pense que vous vous posez au sujet de ce dernier. Dans ce cas, je ferais quelque chose comme
template<typename T>
bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64
Alors ma question est: Est-il possible de dire au compilateur qu'un long int est aussi un int64_t, tout comme long int est
Ceci est une bonne question ou un problème, mais je soupçonne que la réponse est NON.
En outre, un long int
ne peut pas être un long long int
.
# if __WORDSIZE == 64 typedef long int int64_t; # else __extension__ typedef long long int int64_t; # endif
Je crois que c'est libc. Je suppose que vous voulez aller plus loin.
Dans les deux 32 bits de la compilation avec GCC (et 32 ??et 64 bits MSVC), le sortie du programme sera:
int: 0 int64_t: 1 long int: 0 long long int: 1
32 bits Linux utilise le modèle de données ILP32. Entiers, désire ardemment et pointeurs sont 32 bits. Le type 64-bit est un long long
.
documents Microsoft les plages à Type de données Plages . Le dire le long long
est équivalent à __int64
.
Toutefois, le programme résultant d'une sortie de la volonté de compilation GCC 64 bits:
int: 0 int64_t: 1 long int: 1 long long int: 0
Linux 64 bits utilise le modèle de données de LP64
. Longs sont de 64 bits et long long
sont 64 bits. Comme 32 bits, les documents Microsoft les plages à Type de données Ranges long long est encore __int64
.
Là où tout est de 64 bits est un modèle de données ILP64
. Vous devez faire un travail supplémentaire pour obtenir une définition pour votre type de word32
. Voir aussi des journaux comme 64 bits modèles de programmation: Pourquoi LP64
Mais est horriblement hackish et n'échelle pas bien (fonctions réelles de fond, uint64_t, etc) ...
Oui, il y a encore mieux. Les mélanges du CCG et les matches des déclarations qui sont censés prendre 64 types de bits, donc il est facile d'avoir des ennuis, même si vous suivez un modèle de données particulier. Par exemple, ce qui suit provoque une erreur de compilation et vous dit d'utiliser -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);
Il en résulte:
error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'
Alors, ignorer LP64
et changer à:
typedef unsigned long long word64;
Alors, errer sur un gadget ARM IdO 64 bits qui définit LP64
et l'utilisation NEON:
error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'