long long int vs. long int vs. int64_t in C ++
Frage
Ich erlebte einige seltsame Verhalten während C ++ Typ Eigenschaften und haben mein Problem auf diesem schrulligen kleines Problem verengt, für die ich eine Tonne Erklärung geben, da will ich nichts für Fehlinterpretationen offen lassen.
Angenommen, Sie haben ein Programm wie folgt:
#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;
}
In den beiden 32-Bit-Kompilierung mit GCC (und mit 32- und 64-Bit-MSVC), die Ausgabe des Programms wird sein:
int: 0
int64_t: 1
long int: 0
long long int: 1
Doch das Programm aus einer 64-Bit-GCC kompilieren Ausgabe:
int: 0
int64_t: 1
long int: 1
long long int: 0
Das ist merkwürdig, da long long int
ist ein signiertes 64-Bit-Integer und ist für alle Absichten und Zwecke, identisch mit den long int
und int64_t
Typen, so logisch, int64_t
, long int
und long long int
wären gleichwertige Typen - die Anordnung erzeugt wird, wenn diese Art verwendet, ist identisch. Ein Blick auf stdint.h
sagt mir, warum:
# if __WORDSIZE == 64
typedef long int int64_t;
# else
__extension__
typedef long long int int64_t;
# endif
In einer 64-Bit-Kompilierung, int64_t
ist long int
, kein long long int
(offensichtlich).
Das Update für diese Situation ist recht einfach:
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
Aber das ist schrecklich hackish und skaliert nicht gut (tatsächliche Funktionen der Substanz, uint64_t
, etc). Meine Frage ist also: Gibt es eine Möglichkeit, den Compiler zu sagen, dass ein long long int
ist auch ein int64_t
, wie long int
ist
Meine erste Gedanken sind, dass dies nicht möglich ist, aufgrund der Art und Weise C / C ++ Typdefinitionen arbeiten. Es gibt keine Art und Weise Art Gleichwertigkeit der grundlegenden Datentypen an den Compiler zu spezifizieren, da die Aufgabe des Compilers (und ermöglicht, dass eine Menge Dinge brechen könnte) und typedef
geht nur in eine Richtung.
Ich bin auch auch nicht darum, hier eine Antwort bekommen, da dies ein super-duper Rand Fall, dass ich nicht Verdächtiger jemand jemals kümmern, wenn die Beispiele nicht schrecklich ersonnen (bedeutet, dass diese Gemeinschaft sein sollte Wiki?).
anhänge : Der Grund, warum ich bin mit partieller Vorlage Spezialisierung statt einem einfacheren Beispiel wie:
void go(int64_t) { }
int main()
{
long long int x = 2;
go(x);
return 0;
}
ist, dass dieses Beispiel noch kompilieren, da long long int
zu einem int64_t
implizit konvertierbar ist.
anhängen : Die einzige Antwort, so weit geht davon aus, dass ich wissen will, ob ein Typ 64-Bit. Ich wollte nicht zu verleiten Menschen zu denken, dass ich über die Pflege und wahrscheinlich sollte weitere Beispiele zur Verfügung gestellt haben, wo dieses Problem manifestiert sich.
template <typename T>
struct some_type_trait : boost::false_type { };
template <>
struct some_type_trait<int64_t> : boost::true_type { };
In diesem Beispiel wird some_type_trait<long int>
ein boost::true_type
, aber some_type_trait<long long int>
wird nicht sein. Während dieser Sinn in C ++ die Idee der Typen macht, ist es nicht wünschenswert ist.
Ein weiteres Beispiel einen Qualifizierer wie same_type
verwendet (die Verwendung in C ist sehr verbreitet ++ 0x Konzepte):
template <typename T>
void same_type(T, T) { }
void foo()
{
long int x;
long long int y;
same_type(x, y);
}
versagt, dieses Beispiel zu kompilieren, da C ++ (korrekt) erkennt, dass die Arten unterschiedlich sind. g ++ fehl mit einem Fehler wie zu kompilieren. keine passenden Funktionsaufruf same_type(long int&, long long int&)
Ich möchte betonen, dass ich verstehe, Warum dies geschieht, aber ich bin auf der Suche für eine Abhilfe, die mich nicht Code zwingt zu wiederholen alle über dem Platz.
Lösung
Sie müssen nicht auf 64-Bit gehen, so etwas zu sehen. Betrachten Sie int32_t
über gemeinsame 32-Bit-Plattformen. Es könnte als typedef
oder als int
long
'ed werden, aber natürlich nur eine der beiden auf einmal. int
und long
sind natürlich unterschiedliche Typen.
Es ist nicht schwer zu sehen, dass es keine Abhilfe, die int == int32_t == long
auf 32-Bit-Systemen macht. Aus dem gleichen Grund gibt es keine Möglichkeit long == int64_t == long long
auf 64-Bit-Systeme zu machen.
Wenn Sie könnten, die möglichen Folgen eher schmerzhaft für Code wäre, dass überladene foo(int)
, foo(long)
und foo(long long)
-?! Plötzlich sie zwei Definitionen für die gleiche Überlastung haben würde
Die richtige Lösung ist, dass Ihre Template-Code sollte in der Regel nicht auf eine genaue Art angewiesen sein, sondern auf die Eigenschaften dieses Typs. Die ganze same_type
Logik noch in Ordnung für bestimmte Fälle geben könnte:
long foo(long x);
std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);
d. Wird die Überlastung foo(int64_t)
nicht definiert, wenn es genau die gleichen wie foo(long)
.
[Bearbeiten] Mit C ++ 11, haben wir jetzt einen Standard-Weg, dies zu schreiben:
long foo(long x);
std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);
Andere Tipps
Möchten Sie wissen wollen, ob eine Art vom gleichen Typ wie int64_t oder wollen Sie wissen, ob etwas 64 Bits ist? Basierend auf Ihre vorgeschlagene Lösung, ich glaube, Sie über die letzteren sind zu fragen. In diesem Fall würde ich so etwas wie
template<typename T>
bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64
Also meine Frage ist: Gibt es eine Möglichkeit, den Compiler zu sagen, dass ein langen, langes int ist auch ein int64_t, wie lange int
Dies ist eine gute Frage oder ein Problem, aber ich vermute, die Antwort NEIN ist.
Auch ein long int
kann kein long long int
sein.
# if __WORDSIZE == 64 typedef long int int64_t; # else __extension__ typedef long long int int64_t; # endif
Ich glaube, dies ist libc. Ich vermute, Sie tiefer gehen wollen.
In den beiden 32-Bit-Kompilierung mit GCC (und mit 32- und 64-Bit-MSVC), die Ausgabe des Programms wird sein:
int: 0 int64_t: 1 long int: 0 long long int: 1
32-Bit-Linux verwendet das ILP32 Datenmodell. Die ganzen Zahlen, longs und Zeiger sind 32-Bit. Der 64-Bit-Typ ist ein long long
.
Microsoft Dokumente die Bereiche unter Datentyp Rang . Das sagt die long long
entspricht __int64
.
Doch das Programm aus einer 64-Bit-GCC kompilieren Ausgabe:
int: 0 int64_t: 1 long int: 1 long long int: 0
64-Bit-Linux verwendet das LP64
Datenmodell. Longs sind 64-Bit und long long
sind 64-bit. Wie bei den 32-Bit, die Bereiche Microsoft-Dokumente unter Datentyp Rang und lange, lange noch __int64
.
Es gibt ein ILP64
Datenmodell, wo alles 64-bit ist. Sie haben einige zusätzliche Arbeit zu tun, um eine Definition für Ihren word32
Typen zu erhalten. Siehe auch Papiere wie 64-Bit Programmiermodelle: Warum LP64
Aber das ist schrecklich hackish und skaliert nicht gut (tatsächliche Funktionen der Substanz, uint64_t, etc) ...
Ja, es kommt noch besser. GCC-Mischungen und Streichhölzer Erklärungen, die angeblich 64-Bit-Typen zu nehmen, so dass ihre leicht in Schwierigkeiten zu bringen, auch wenn Sie ein bestimmtes Datenmodell folgen. Beispielsweise verursacht die folgende einen Compiler-Fehler und sagen Sie, zu verwenden -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);
Es ergibt sich:
error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'
Also, ignorieren LP64
und ändern Sie es an:
typedef unsigned long long word64;
Dann wandern über auf einen 64-Bit-ARM-IoT-Gadget, das definiert LP64
und Verwendung NEON:
error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'