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 delong 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
.1 votes
Personne ne l'a encore mentionné, mais au cas où cela aurait été oublié :
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 à un type qui existe déjà)3 votes
Une déclaration importante manque dans les réponses/commentaires, ce qui m'a aidé lorsque cette bizarrerie m'a frappé : N'utilisez jamais de types à taille fixe pour spécialiser de manière fiable les modèles. Utilisez toujours des types de base et couvrez tous les cas possibles (même si vous utilisez des types à taille fixe pour instancier ces modèles). Tous les cas possibles signifie : si vous devez instancier avec
int16_t
puis spécialiser avecshort
etint
et vous serez couvert. (et avecsigned char
si vous êtes d'humeur aventureuse)