Ich habe bei der Verwendung von C ++ - Typmerkmalen ein merkwürdiges Verhalten festgestellt und mein Problem auf dieses skurrile kleine Problem eingegrenzt, für das ich eine Menge Erklärungen geben werde, da ich nichts für Fehlinterpretationen offen lassen möchte.
Angenommen, Sie haben ein Programm wie dieses:
#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;
}
Bei der 32-Bit-Kompilierung mit GCC (und mit 32- und 64-Bit-MSVC) lautet die Ausgabe des Programms:
int: 0
int64_t: 1
long int: 0
long long int: 1
Das aus einer 64-Bit-GCC-Kompilierung resultierende Programm gibt jedoch Folgendes aus:
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 dem long int
und int64_t
Typen, so logisch, int64_t
, long int
und long long int
gleichwertige Typen sein würde - die Anordnung erzeugt wird, wenn diese Arten mit identischem wird. Ein Blick zeigt stdint.h
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 (offensichtlich) long int
kein long long int
.
Die Lösung für diese Situation ist ziemlich einfach:
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
Aber das ist schrecklich hackisch und lässt sich nicht gut skalieren (tatsächliche Substanzfunktionen uint64_t
usw.). Meine Frage ist also: Gibt es eine Möglichkeit, dem Compiler zu sagen, dass a long long int
auch a ist int64_t
, genau wie es long int
ist?
Meine ersten Gedanken sind, dass dies aufgrund der Funktionsweise von C / C ++ - Typdefinitionen nicht möglich ist. Es gibt keine Möglichkeit, die Typäquivalenz der Basisdatentypen für den Compiler anzugeben, da dies die Aufgabe des Compilers ist (und dies viele Dinge beschädigen kann) und typedef
nur in eine Richtung geht.
Ich bin auch nicht allzu besorgt darüber, hier eine Antwort zu bekommen, da dies ein Super-Duper-Randfall ist, von dem ich nicht vermute, dass sich jemals jemand darum kümmern wird, wenn die Beispiele nicht schrecklich erfunden sind (bedeutet das, dass dies ein Community-Wiki sein sollte?) .
Anhängen : Der Grund, warum ich eine teilweise Vorlagenspezialisierung anstelle eines einfacheren Beispiels verwende, wie:
void go(int64_t) { }
int main()
{
long long int x = 2;
go(x);
return 0;
}
ist, dass das Beispiel noch kompiliert wird, da long long int
es implizit in ein konvertierbar ist int64_t
.
Anhängen : Die einzige Antwort geht bisher davon aus, dass ich wissen möchte, ob ein Typ 64-Bit ist. Ich wollte die Leute nicht irreführen, zu denken, dass mir das wichtig ist, und hätte wahrscheinlich mehr Beispiele dafür liefern sollen, wo sich dieses Problem manifestiert.
template <typename T>
struct some_type_trait : boost::false_type { };
template <>
struct some_type_trait<int64_t> : boost::true_type { };
In diesem Beispiel some_type_trait<long int>
wird a sein , wird es boost::true_type
aber some_type_trait<long long int>
nicht sein. Dies ist zwar in C ++ 's Vorstellung von Typen sinnvoll, aber nicht wünschenswert.
Ein anderes Beispiel ist die Verwendung eines Qualifikationsmerkmals wie same_type
(das in C ++ 0x-Konzepten häufig verwendet wird):
template <typename T>
void same_type(T, T) { }
void foo()
{
long int x;
long long int y;
same_type(x, y);
}
Dieses Beispiel kann nicht kompiliert werden, da C ++ (korrekt) erkennt, dass die Typen unterschiedlich sind. g ++ kann nicht mit einem Fehler wie dem folgenden kompiliert werden: kein übereinstimmender Funktionsaufruf same_type(long int&, long long int&)
.
Ich möchte betonen, dass ich verstehe, warum dies geschieht, aber ich suche nach einer Problemumgehung, die mich nicht zwingt, Code überall zu wiederholen.
sizeof
jeden Typ die gleichen Ergebnisse ? Vielleicht behandelt der Compiler die Größelong long int
anders.<cstdint>
, also ist es vielleicht die Tatsache, dass es "dies ist eine Erweiterung" (was es ist) zu sagen hat.--std=c++0x
. Und ja ,sizeof(long long int) == sizeof(long int) == sizeof(int64_t) == 8
.long
undlong long
sind verschiedene Typen (auch wenn sie die gleiche Größe und Darstellung haben).int64_t
ist immer ein Alias für einen anderen vorhandenen Typ (trotz seines Namens werdentypedef
keine neuen Typen erstellt, sondern nur ein Alias für einen bereits vorhandenen)int16_t
, dann spezialisieren Sie sich aufshort
undint
und Sie sind abgesichert. (und mit,signed char
wenn Sie sich abenteuerlustig fühlen)Antworten:
Sie müssen nicht auf 64-Bit umsteigen, um so etwas zu sehen. Betrachten Sie
int32_t
auf gängigen 32-Bit-Plattformen. Es kanntypedef
alsint
oder alslong
, aber offensichtlich nur einer der beiden gleichzeitig sein.int
undlong
sind natürlich verschiedene Typen.Es ist nicht schwer zu erkennen, dass es
int == int32_t == long
auf 32-Bit-Systemen keine Problemumgehung gibt . Aus dem gleichen Grund gibt es keine Möglichkeit,long == int64_t == long long
auf 64-Bit-Systemen zu machen .Wenn Sie könnten, wären die möglichen Konsequenzen für überlasteten Code ziemlich schmerzhaft
foo(int)
,foo(long)
undfoo(long long)
- plötzlich hätten sie zwei Definitionen für dieselbe Überladung?!Die richtige Lösung besteht darin, dass sich Ihr Vorlagencode normalerweise nicht auf einen genauen Typ stützen sollte, sondern auf die Eigenschaften dieses Typs. Die gesamte
same_type
Logik könnte für bestimmte Fälle noch in Ordnung sein:Das heißt, die Überlastung
foo(int64_t)
ist nicht definiert, wenn sie genau die gleiche ist wiefoo(long)
.[Bearbeiten] Mit C ++ 11 haben wir jetzt eine Standardmethode, um dies zu schreiben:
quelle
sizeof()
long
undint
sind identisch,std::is_same<long, int>::value
kehren aber zurückfalse
. Gleiche Verrücktheit unter AppleClang 9.1 unter OSX HighSierra.Möchten Sie wissen, ob ein Typ mit int64_t identisch ist, oder möchten Sie wissen, ob etwas 64-Bit ist? Aufgrund Ihrer vorgeschlagenen Lösung fragen Sie wahrscheinlich nach letzterer. In diesem Fall würde ich so etwas tun
quelle
return
und ein Semikolon?sizeof
dafür verwenden.short
= 16 Bit,long
= 32 Bit undint
= native Größe. In diesen Tagen des 64-Bit,int
undlong
nicht Mittel nichts mehr.short
ist mindestens 16 Bit,int
ist mindestens 16 Bit undlong
ist mindestens 32 Bit, mit (schlampige Notation folgt) kurz <= int <= lang. Die "alten Zeiten", auf die Sie sich beziehen, existierten nie; Es gab immer Abweichungen innerhalb der durch die Sprache auferlegten Einschränkungen. Der Irrtum "Die ganze Welt ist ein x86" ist genauso gefährlich wie der ältere "Die ganze Welt ist ein VAX-Irrtum"Dies ist eine gute Frage oder ein gutes Problem, aber ich vermute, die Antwort lautet NEIN.
Auch a
long int
darf nicht a seinlong long int
.Ich glaube das ist libc. Ich vermute, Sie wollen tiefer gehen.
32-Bit-Linux verwendet das ILP32-Datenmodell. Ganzzahlen, Longs und Zeiger sind 32-Bit. Der 64-Bit-Typ ist a
long long
.Microsoft dokumentiert die Bereiche unter Datentypbereiche . Das Sprichwort
long long
ist gleichbedeutend mit__int64
.64-Bit-Linux verwendet das
LP64
Datenmodell. Longs sind 64-Bit undlong long
64-Bit. Wie bei 32-Bit dokumentiert Microsoft die Bereiche unter Datentypbereiche und ist noch lange lang__int64
.Es gibt ein
ILP64
Datenmodell, bei dem alles 64-Bit ist. Sie müssen einige zusätzliche Arbeiten ausführen, um eine Definition für Ihrenword32
Typ zu erhalten. Siehe auch Artikel wie 64-Bit-Programmiermodelle: Warum LP64?Ja, es wird noch besser. GCC mischt und stimmt Deklarationen ab, die 64-Bit-Typen annehmen sollen, sodass es leicht zu Problemen kommt, obwohl Sie einem bestimmten Datenmodell folgen. Das Folgende verursacht beispielsweise einen Kompilierungsfehler und fordert Sie auf, Folgendes zu verwenden
-fpermissive
:Es fuehrt zu:
Ignorieren Sie es
LP64
und ändern Sie es in:Wechseln Sie dann zu einem 64-Bit-ARM-IoT-Gadget, das
LP64
NEON definiert und verwendet:quelle