105 votes

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

J'ai fait l'expérience d'un comportement étrange lors de l'utilisation des traits de type C++ et j'ai réduit mon problème à ce petit problème excentrique pour lequel je vais donner une tonne d'explications puisque 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 donnera comme résultat :

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

C'est curieux, car long long int est un nombre entier signé de 64 bits et est, à toutes fins utiles, identique à l'élément long int et int64_t des types, donc logiquement, int64_t , long int et long long int seraient 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 est long int et non 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 une méthode horriblement bricolée et qui ne s'adapte pas bien (fonctions réelles de substance), uint64_t etc). Donc ma question est : Existe-t-il un moyen d'indiquer au compilateur qu'un fichier long long int est également un int64_t tout comme long int est ?


Je pense tout d'abord que cela n'est pas possible, en raison de la façon dont les définitions de type C/C++ fonctionnent. Il n'y a pas moyen de spécifier l'équivalence des types de données de base au compilateur, puisque c'est le travail du compilateur (et permettre cela pourrait casser beaucoup de choses) et typedef ne va que dans un sens.

Je ne suis pas non plus trop préoccupé par l'obtention d'une réponse ici, puisqu'il s'agit d'un cas limite super-duper dont je ne pense pas que quelqu'un se souciera un jour lorsque les exemples ne seront pas horriblement artificiels (cela signifie-t-il que cela devrait être un wiki communautaire ?).


Ajouter : La raison pour laquelle j'utilise la spécialisation partielle des modèles 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 sera toujours compilé, puisque long long int est implicitement convertible en un int64_t .


Ajouter : La seule réponse jusqu'à présent suppose que je veux savoir si un type est de 64 bits. Je ne voulais pas induire les gens en erreur en leur faisant croire que je me soucie de cela et j'aurais probablement dû fournir plus d'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 pero some_type_trait<long long int> ne le sera pas. Bien que cela soit logique dans l'idée de types du C++, ce n'est pas souhaitable.

Un autre exemple est l'utilisation d'un qualificatif comme same_type (dont l'utilisation est assez courante 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 compile pas, car le C++ voit (correctement) que les types sont différents. g++ ne compilera pas avec une erreur du type : aucun appel de fonction correspondant. same_type(long int&, long long int&) .

Je tiens à souligner que je comprends pourquoi C'est ce qui se passe, mais je cherche une solution de contournement qui ne m'oblige pas à répéter du code partout.

0 votes

Par curiosité, votre programme d'exemple donne-t-il les mêmes résultats pour l'option sizeof chaque type ? Peut-être que le compilateur traite la taille de long long int différemment.

0 votes

Avez-vous compilé avec C++0x activé ? Le C++03 n'a pas <cstdint> Le fait qu'il faille dire "ceci est une extension" (ce qui est le cas) le freine peut-être.

0 votes

Oui, j'aurais probablement dû préciser que j'utilise --std=c++0x . Et oui, sizeof(long long int) == sizeof(long int) == sizeof(int64_t) == 8 .

54voto

MSalters Points 74024

Il n'est pas nécessaire de passer en 64 bits pour voir quelque chose comme ça. Pensez à int32_t sur les plateformes 32 bits courantes. Cela peut être typedef ed comme int ou en tant que long mais évidemment, 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 rende int == int32_t == long sur les systèmes 32 bits. Pour la même raison, il n'y a pas moyen de rendre long == int64_t == long long sur les systèmes 64 bits.

Si vous le pouviez, les conséquences possibles seraient plutôt douloureuses pour le code qui surchargeait foo(int) , foo(long) et foo(long long) - soudainement, ils auraient deux définitions pour la même surcharge !

La solution correcte est que votre code de modèle ne doit généralement pas s'appuyer sur un type précis, mais sur les propriétés de ce type. L'ensemble du same_type La logique pourrait encore être acceptable 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 la surcharge foo(int64_t) n'est pas défini lorsqu'il est exactement la même chose que foo(long) .

[modifier] Avec C++11, nous avons maintenant une façon 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>);

1 votes

La triste nouvelle est que, par exemple, sur MSVC19 (2017) 64bit sizeof() long et int est identique, mais std::is_same<long, int>::value renvoie à false . Même bizarrerie avec AppleClang 9.1 sur OSX HighSierra.

3 votes

@Ax3l : Ce n'est pas bizarre. Pratiquement tous les compilateurs depuis ISO C 90 ont au moins une telle paire.

0 votes

C'est vrai, ce sont des types distincts.

7voto

Logan Capaldo Points 22145

Voulez-vous savoir si un type est le même que int64_t ou voulez-vous savoir si quelque chose est de 64 bits ? D'après la solution que vous proposez, je pense que votre question porte sur le second cas. Dans ce cas, je ferais quelque chose comme

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

1 votes

Ne manque-t-il pas un return et un point-virgule ?

0 votes

Ce n'est pas du tout ce que je recherche. L'exemple a été fourni pour montrer une façon dont l'erreur peut se manifester, et non comme une exigence réelle.

1 votes

Pourtant, vous devriez utiliser sizeof pour ça.

3voto

jww Points 9514

Ma question est donc la suivante : existe-t-il un moyen d'indiquer au compilateur qu'un long long int est également un int64_t, tout comme le long int ?

C'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 soupçonne que vous voulez aller plus loin.

Dans les deux compilations 32 bits avec GCC (et avec MSVC 32 et 64 bits), l'élément 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 32 bits. Le type 64 bits est un long long .

Microsoft documente les gammes à Plages de types de données . Le disent les long long est équivalent à __int64 .

Cependant, le programme résultant d'une compilation GCC 64 bits donnera comme résultat :

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

Linux 64 bits utilise le LP64 modèle de données. Les longs sont 64 bits et long long sont de 64 bits. Comme pour le 32 bits, Microsoft documente les plages à l'adresse suivante Plages de types de données et longtemps encore __int64 .

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


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

Oui, c'est encore mieux. GCC mélange et assortit 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, la déclaration suivante provoque une erreur de compilation et vous demande d'utiliser des types 64 bits. -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 le changer en :

typedef unsigned long long word64;

Ensuite, passez à un gadget IoT ARM 64 bits qui définit LP64 et utiliser NEON :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X