web-dev-qa-db-fra.com

long long int vs long int vs int64_t en C ++

En utilisant des caractéristiques de type C++, j’ai eu un comportement étrange et j’ai réduit mon problème à ce petit problème insolite 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 générera:

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

C'est curieux, car long long int Est un entier signé de 64 bits et est, à toutes fins utiles, identique aux types long int Et int64_t, Donc logiquement, int64_t, long int Et long long int Seraient des types équivalents - l'ensemble généré lors de l'utilisation de ces types est identique. Un coup d’œil 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 Est long 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 ceci est horriblement bidon et n’a pas une bonne échelle (fonctions réelles de la substance, uint64_t, Etc.). Donc ma question est: Y a-t-il un moyen de dire au compilateur qu'un long long int Est aussi un int64_t, Tout comme long int?


Ma pensée initiale est que cela n’est pas possible, en raison du fonctionnement des définitions de types C/C++. Il n’existe pas de moyen de spécifier l’équivalence de types des types de données de base au compilateur, car c’est le travail du compilateur (et permettre cela pourrait casser beaucoup de choses) et typedef ne va que dans un sens.

Je ne cherche pas non plus trop à obtenir une réponse ici, car il s'agit d'un cas extrêmement controversé dans Edge qui, à mon avis, ne préoccupera personne si les exemples ne sont pas horriblement artificiels (cela signifie-t-il que cela devrait être un wiki de la communauté?) .


Append: La raison pour laquelle j'utilise une spécialisation de modèle partielle au lieu d'un exemple plus simple, tel que:

void go(int64_t) { }

int main()
{
    long long int x = 2;
    go(x);
    return 0;
}

est-ce que cet exemple sera toujours compilé, puisque long long int est implicitement convertible en int64_t.


Append: La seule réponse à ce jour suppose que je veuille savoir si un type est en 64 bits. Je ne voulais pas induire les gens en erreur en leur faisant croire que cela m'importait et que j'aurais probablement dû fournir davantage d'exemples montrant 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 le sera pas. Bien que cela ait du sens dans l'idée de types de C++, ce n'est pas souhaitable.

Un autre exemple utilise un qualificatif comme same_type (Qui est assez commun à 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 à compiler, car C++ (correctement) voit que les types sont différents. g ++ échouera à la compilation avec une erreur du type: pas d'appel de fonction correspondant same_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 m'oblige pas à répéter le code un peu partout. .

78
Travis Gockel

Vous n'avez pas besoin d'aller en 64 bits pour voir quelque chose comme ça. Considérons int32_t Sur les plates-formes 32 bits courantes. Il peut s'agir de typedef 'ed comme int ou de long, mais évidemment d'un seul des deux à la fois. int et long sont bien sûr des types distincts.

Il n'est pas difficile de voir qu'il n'y a pas de solution de contournement qui rend int == int32_t == long Sur les systèmes 32 bits. Pour la même raison, il n’ya aucun moyen de créer long == int64_t == long long Sur des systèmes 64 bits.

Si vous le pouviez, les conséquences possibles seraient plutôt pénibles pour le code surchargé foo(int), foo(long) et foo(long long) - du coup, ils auraient deux définitions pour la même surcharge. ?!

La solution correcte est que votre code de modèle ne devrait généralement pas s'appuyer sur un type précis, mais sur les propriétés de ce type. La logique entière same_type Peut toujours ê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 quand exactement identique à foo(long).

[edit] Avec C++ 11, nous avons maintenant un moyen standard d'écrire ceci:

long foo(long x);
std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);
47
MSalters

Voulez-vous savoir si un type est du même type que int64_t ou voulez-vous savoir si quelque chose est en 64 bits? Sur la base de votre solution proposée, 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
5
Logan Capaldo

Ma question est donc la suivante: y a-t-il un moyen de dire au compilateur qu'un int long long est aussi un int64_t, tout comme long int est?

C'est une bonne question ou un bon problème, mais je soupçonne que la réponse est NON.

Aussi un long int peut ne 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 soupçonne que vous voulez aller plus loin.

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

Linux 32 bits utilise le modèle de données ILP32. Les entiers, les longs et les pointeurs sont en 32 bits. Le type 64 bits est un long long.

Microsoft documente les plages situées à plages de types de données . Le dire le long long est équivalent à __int64.

Cependant, le programme résultant d'une compilation GCC 64 bits générera:

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

Linux 64 bits utilise le LP64 modèle de données. Les longueurs sont 64 bits et long long sont 64 bits. Comme pour le 32 bits, Microsoft documente les plages situées à plages de types de données et long long is __int64.

Il y a un ILP64 modèle de données où tout est en 64 bits. Vous devez faire un travail supplémentaire pour obtenir une définition de votre Word32 type. Voir également des articles tels que Modèles de programmation 64 bits: Pourquoi LP64?


Mais ceci est horriblement bidon et ne s’adapte pas bien (fonctions réelles de la substance, uint64_t, etc) ...

Ouais, ça va encore mieux. GCC mélange et met en correspondance les déclarations supposées prendre des types 64 bits. Il est donc facile de se mettre en difficulté 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*'

Donc, ignorez LP64 et changez-le en:

typedef unsigned long long Word64;

Ensuite, dirigez-vous vers un gadget IoT ARM) 64 bits qui définit LP64 et utilisez NEON:

error: invalid conversion from `Word64* {aka long long unsigned int*}' to `uint64_t*'
1
jww