2016-04-29 5 views
4

Der folgende Code ohne Warnung erstellt:Bit Verschiebung führt zu seltsamer Typkonvertierung

std::uint16_t a = 12; 
std::uint16_t b = a & 0x003f; 

jedoch zusammen mit dem bitweisen eine Bit-Verschiebung durchführte und führt zu einer ‚impliziten Guss Warnung‘:

std::uint16_t b = (a & 0x003f) << 10; // Warning generated. 

Sowohl gcc als auch clang beklagen, dass es eine implizite Konvertierung von int zu uint16_t gibt, aber ich verstehe nicht, warum die Einführung der Bitverschiebung dazu führen würde, dass der Ausdruck der rechten Hand plötzlich zu int wird.

EDIT: Für Clang kompilierte ich mit den -std=c++14 -Weverything Flags; für gcc habe ich mit den -std=c++14 -Wall -Wconversion Flags kompiliert.

+0

Der Ausdruck wird in beiden Fällen als "int" ausgewertet. Würden Sie für diese Warnung Compiler-Einstellungen (vielleicht ein Online-Beispiel) bereitstellen? Ich bekomme diese Warnung nicht in einem schnellen Experiment, weder von GCC noch von – AnT

+0

@AnT Probieren Sie "-Wconversion". Sie sollten dann die Warnung sehen. – Andrew

+1

_ "Wenn der Operand, der an einen arithmetischen Operator übergeben wird, ein Integral- oder Unscoped-Aufzählungstyp ist, dann wird der Operand vor jeder anderen Aktion (aber nach der lvalue-to-rvalue-Konvertierung, falls zutreffend) vollständig befördert." _ [Http: // en.cppreference.com/w/cpp/language/operator_arithmetic](http://en.cppreference.com/w/cpp/language/operator_arithmetic) Siehe auch [this] (http://stackoverflow.com/questions/ 36925291/Bit-Shifting-Links-und-Verwerfen-Bits? Noredirect = 1 # comment61414105_36925291). – ZDF

Antwort

1

noch nicht ich sehe, warum das Einführen der Bitverschiebung den Ausdruck der rechten Hand veranlassen würde, plötzlich zu einem int auszuwerten.

Ich denke, dass Sie die Warnung falsch interpretieren. In beiden Fällen wird der Ausdruck int ausgewertet, aber im ersten Fall wird das Ergebnis immer in uint16_t passen, im zweiten Fall nicht. Sieht so aus, als ob der Compiler schlau genug ist, dies zu erkennen, und nur im zweiten Fall eine Warnung generiert.

+0

@Ant weil das immer noch in 'uint16_t' passt, kannst du stattdessen binary' or' ausprobieren? – Slava

+0

@Slava: Ja, verstanden. Irgendwie habe ich vergessen, das '&' zu ersetzen. – AnT

+1

'unsigned char a = 1; unsigned char b = (a & 0x01) << 4; 'wird die gleiche Warnung ausgeben. 'unsigned char b = (1 & 0x01) << 4;' wird "intelligent genug" fallen. – ZDF

2

Bei jeder Arithmetik mit Integer-Typen wird immer der Aufstieg auf mindestens (manchmal, aber nicht in diesem Fall mit gcc, unsigned) int vorangestellt. Wie Sie an this example sehen können, gilt dies für Sie zuerst, warnlose Variante auch.

Der wahrscheinlich beste Weg, um diese (zugegebenermaßen oft überraschend) ganzzahligen Promotion-Regeln zu umgehen, wäre eine unsigned int (oder uint32_t auf gemeinsamen Plattformen) von Anfang an zu verwenden.

Wenn Sie nicht oder nicht den größeren Typ verwenden möchten, können Sie einfach static_cast das Ergebnis des gesamten Ausdrucks zurück zu std::uint16_t:

std::uint16_t b = static_cast<std::uint16_t>((a & 0x003f) << 10); 

Dies wird in der RHS-Wert mod richtig 2^16.

1

Von cppreference.com: „Wenn der Operand eines arithmetischen Operator geleitet ist integral oder unscoped Aufzählungstyp, dann, bevor eine andere Aktion (aber nach dem L-Wert-zu-R-Wert Umwandlung, wo zutreffend), der Operand erfährt integrale Förderung . "

Zum Beispiel:

byte a = 1; 
byte b = a << byte(1); 
  1. a und 1 werden gefördert int: int(a) und int(byte(1)).
  2. a ist um eine Position nach links verschoben: int result = int(a) << int(byte(1)) (das Ergebnis ist ein int).
  3. result ist in b gespeichert. Da int breiter ist als byte wird eine Warnung ausgegeben.

Wenn die Operanden konstanter Ausdrücke, ein Compiler könnte in der Lage sein, das Ergebnis zum Zeitpunkt der Kompilierung zu berechnen und eine Warnung iff das Ergebnis nicht ausgestellt in Ziel passen:

byte b = 1 << 1; // no warning: does not exceed 8 bits 
byte b = 1 << 8; // warning: exceeds 8 bits 

Oder mit constexpr:

constexpr byte a = 1; 
byte b = a << 1; // no warning: it fits in 8 bits 
byte b = a << 8; // warning: it does not fit in 8 bits