J'ai eu un comportement étrange en utilisant des traits de type C ++ et j'ai réduit mon problème à ce petit problème bizarre pour lequel je vais donner une tonne d'explications car je ne veux rien laisser ouvert à une mauvaise interprétation.
Disons que vous avez un programme comme celui-ci:
#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 compilations 32 bits avec GCC (et avec MSVC 32 et 64 bits), la sortie du programme sera:
int: 0
int64_t: 1
long int: 0
long long int: 1
Cependant, le programme résultant d'une compilation GCC 64 bits affichera:
int: 0
int64_t: 1
long int: 1
long long int: 0
C'est curieux, car il long long int
s'agit d'un entier 64 bits signé et, à toutes fins utiles, identique aux types long int
et int64_t
, donc logiquement int64_t
, long int
et long long int
serait des types équivalents - l'assemblage généré lors de l'utilisation de ces types est identique. Un regard sur 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 64 bits, int64_t
estlong int
pas un long long int
(évidemment).
La solution à cette situation est assez simple:
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
Mais c'est horriblement hackish et ne s'adapte pas bien (fonctions réelles de la substance uint64_t
, etc.). Ma question est donc la suivante: existe-t-il un moyen de dire au compilateur que a long long int
est aussi a int64_t
, tout commelong int
est?
Mes premières pensées sont que ce n'est pas possible, en raison du fonctionnement des définitions de type C / C ++. Il n'y a pas de 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 permettre que cela puisse casser beaucoup de choses) ettypedef
ne va que dans un sens.
Je ne suis pas non plus trop soucieux d'obtenir une réponse ici, car il s'agit d'un cas super-duper edge dont je ne soupçonne pas que quiconque se souciera jamais lorsque les exemples ne sont pas horriblement artificiels (cela signifie-t-il que cela devrait être un wiki de la communauté?) .
Ajouter : La raison pour laquelle j'utilise la spécialisation partielle des modèles au lieu d'un exemple plus simple comme:
void go(int64_t) { }
int main()
{
long long int x = 2;
go(x);
return 0;
}
est que ledit exemple sera toujours compilé, car il long long int
est implicitement convertible en unint64_t
.
Ajouter : La seule réponse à ce jour suppose que je veux savoir si un type est 64 bits. Je ne voulais pas induire les gens en erreur en leur faisant croire que je me soucie de cela et que j'aurais probablement dû fournir plus d'exemples d'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
, maissome_type_trait<long long int>
ne sera pas. Bien que cela ait du sens dans l'idée des types de C ++, ce n'est pas souhaitable.
Un autre exemple consiste à utiliser un qualificatif comme same_type
(qui est assez courant à utiliser dans les concepts C ++ 0x):
template <typename T>
void same_type(T, T) { }
void foo()
{
long int x;
long long int y;
same_type(x, y);
}
Cet exemple ne parvient pas à se compiler, car C ++ voit (correctement) que les types sont différents. g ++ ne parviendra pas à se compiler avec une erreur comme: aucun appel de fonction correspondantsame_type(long int&, long long int&)
.
Je tiens à souligner que je comprends pourquoi cela se produit, mais je recherche une solution de contournement qui ne me force pas à répéter le code partout.
sizeof
chaque type? Peut-être que le compilateur traite la taille delong long int
différemment.<cstdint>
, alors peut-être que le fait qu'il doive dire "ceci est une extension" (ce que c'est) le déjoue.--std=c++0x
. Et oui,sizeof(long long int) == sizeof(long int) == sizeof(int64_t) == 8
.long
etlong long
sont des types distincts (même s'ils ont la même taille et la même représentation).int64_t
est toujours un alias pour un autre type existant (malgré son nom,typedef
ne crée pas de nouveaux types, il donne juste un alias à celui qui existe déjà)int16_t
, spécialisez-vous avecshort
etint
et vous serez couvert. (et avecsigned char
si vous vous sentez aventureux)Réponses:
Vous n'avez pas besoin d'aller en 64 bits pour voir quelque chose comme ça. Pensez
int32_t
aux plates-formes 32 bits courantes. Il peut êtretypedef
«édité commeint
ou comme unlong
, mais évidemment seulement un des deux à la fois.int
etlong
sont bien sûr des types distincts.Il n'est pas difficile de voir qu'il n'y a pas de solution de contournement
int == int32_t == long
sur les systèmes 32 bits. Pour la même raison, il n'y a aucun moyen de fairelong == int64_t == long long
sur des systèmes 64 bits.Si vous le pouviez, les conséquences possibles seraient plutôt douloureuses pour un code surchargé
foo(int)
,foo(long)
etfoo(long long)
- du coup, ils auraient deux définitions pour la même surcharge?!La solution correcte est que le code de votre modèle ne doit généralement pas reposer sur un type précis, mais sur les propriétés de ce type. Toute la
same_type
logique pourrait encore être OK pour des cas spécifiques:long foo(long x); std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);
C'est-à-dire que la surcharge
foo(int64_t)
n'est pas définie lorsqu'elle est exactement la même quefoo(long)
.[modifier] Avec C ++ 11, nous avons maintenant une manière standard d'écrire ceci:
long foo(long x); std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);
[modifier] Ou C ++ 20
long foo(long x); int64_t foo(int64_t) requires (!std::is_same_v<int64_t, long>);
la source
sizeof()
long
etint
est identique, maisstd::is_same<long, int>::value
revientfalse
. Même bizarrerie sur AppleClang 9.1 sur OSX HighSierra.Voulez-vous savoir si un type est du même type que int64_t ou voulez-vous savoir si quelque chose est 64 bits? Sur la base de la solution que vous proposez, je pense que vous vous interrogez sur cette dernière. Dans ce cas, je ferais quelque chose comme
template<typename T> bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64
la source
return
et un point-virgule?sizeof
pour cela.short
= 16 bits,long
= 32 bits etint
= taille native. En ces jours de 64 bits,int
etlong
ne signifie plus rien.short
est d' au moins 16 bits, d'int
au moins 16 bits et d'long
au moins 32 bits, avec (la notation bâclée suit) short <= int <= long. Les «vieux jours» dont vous parlez n'ont jamais existé; il y a toujours eu des variations dans les restrictions imposées par la langue. L'erreur "Tout le monde est un x86" est tout aussi dangereuse que l'ancienne "Tout le monde est une erreur VAX.C'est une bonne question ou un problème, mais je soupçonne que la réponse est NON.
De plus, a
long int
peut ne pas être unlong long int
.Je crois que c'est libc. Je soupçonne que vous voulez aller plus loin.
Linux 32 bits utilise le modèle de données ILP32. Les entiers, les longs et les pointeurs sont de 32 bits. Le type 64 bits est un
long long
.Microsoft documente les plages dans les plages de types de données . Le dire
long long
est équivalent à__int64
.Linux 64 bits utilise le
LP64
modèle de données. Les longs sont 64 bits etlong long
64 bits. Comme avec 32 bits, Microsoft documente les plages aux plages de types de données et long long est toujours__int64
.Il existe un
ILP64
modèle de données où tout est 64 bits. Vous devez faire un travail supplémentaire pour obtenir une définition de votreword32
type. Voir également des articles tels que Modèles de programmation 64 bits: Pourquoi LP64?Ouais, ça va encore mieux. GCC mélange et associe les déclarations qui sont censées prendre des types 64 bits, il est donc facile d'avoir des problèmes même si vous suivez un modèle de données particulier. Par exemple, ce qui suit provoque une erreur de compilation et vous indique 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, ignorez-le
LP64
et changez-le en:typedef unsigned long long word64;
Ensuite, dirigez-vous vers un gadget ARM IoT 64 bits qui définit
LP64
et utilise NEON:error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'
la source