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.

Était-ce utile?

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*'
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top