2013-07-12 10 views
10

Abschnitt 9.6/3 in C++ 11 ist ungewöhnlich klar: "Eine nicht-const Referenz soll nicht an ein Bit-Feld gebunden sein." Was ist die Motivation hinter diesem Verbot?Warum sind nichtkonstante Verweise auf Bitfelder verboten?

Ich verstehe, dass es nicht möglich ist, eine Referenz direkt an ein Bitfeld zu binden. Aber wenn ich so etwas erkläre,

struct IPv4Header { 
    std::uint32_t version:4,   // assumes the IPv4 Wikipedia entry is correct 
       IHL:4, 
       DSCP:6, 
       ECN:2, 
       totalLength:16; 
}; 

Warum kann ich das nicht sagen?

IPv4Header h; 

auto& ecn = h.ECN; 

würde ich der zugrunde liegende Code eigentlich erwartet, dass auf die gesamten std::uint32_t zu binden, die die Bits enthalten Ich habe Interesse an, und ich würde erwarten, dass Lese- und Schreiboperationen Code erzeugen, um die entsprechende Maskierung zu tun. Das Ergebnis könnte groß und langsam sein, aber es scheint mir, dass es funktionieren sollte. Dies würde mit der Art und Weise im Einklang die Norm sagen, dass Verweise auf const bitfields Arbeit (wieder von 9.6/3):

Wenn der Initialisierer für eine Referenz vom Typ const T & ein L-Wert ist, der auf ein Bit verweist -Feld, die Referenz ist an eine temporäre initialisiert an halten Sie den Wert des Bit-Feld; Die Referenz ist nicht direkt an das Bitfeld gebunden.

Dies legt nahe, dass das Schreiben in Bitfelder das Problem ist, aber ich sehe nicht, was es ist. Ich betrachtete die Möglichkeit, dass die notwendige Maskierung Rassen in Multithreading-Code einführen könnte, aber pro 1,7/3 werden benachbarte Bitfelder mit einer Breite ungleich null als ein einzelnes Objekt für Multithreading betrachtet. Im obigen Beispiel würden alle Bitfelder in einem IPv4Header Objekt als ein einzelnes Objekt betrachtet werden, so dass Multithread-Code, der versucht, ein Feld zu ändern, während andere Felder gelesen werden, per Definition bereits rassig wäre.

Ich bin eindeutig etwas fehlt. Was ist es?

Antwort

7

Nicht-konstante Referenzen können nicht an Bit-Felder gebunden werden, aus dem gleichen Grund können Zeiger nicht auf Bit-Felder zeigen.

Während nicht angegeben ist, ob Referenzen Speicher belegen, ist es klar, dass sie in nicht-trivialen Fällen als verkappte Zeiger implementiert sind, und diese Implementierung von Referenzen ist "beabsichtigt" von den Autoren der Sprache. Und genau wie Zeiger müssen Referenzen auf eine adressierbare Speichereinheit zeigen. Es ist nicht möglich, eine nicht konstante Referenz an eine Speichereinheit zu binden, die nicht adressierbar ist. Da nicht-konstante Referenzen eine direkte Bindung erfordern, kann eine nicht-konstante Referenz nicht an ein Bit-Feld gebunden werden.Die einzige Möglichkeit, einen Zeiger/eine Referenz zu erzeugen, die auf Bitfelder zeigen kann, wäre eine Art "Superpointer" zu implementieren, der zusätzlich zu der tatsächlichen Adresse im Speicher auch eine Art Bit-Offset und Bit enthalten würde -Breite-Information, um dem Schreibcode mitzuteilen, welche Bits zu modifizieren sind. Beachten Sie, dass diese zusätzlichen Informationen in allen Datenzeigertypen vorhanden sein müssen, da es in C++ keinen solchen Typ wie "Zeiger/Verweis auf Bitfeld" gibt. Dies entspricht im Wesentlichen der Implementierung eines übergeordneten Speicheradressierungsmodells, ganz losgelöst vom Adressierungsmodell der zugrunde liegenden OS/Hardware-Plattform. Die C++ - Sprache beabsichtigte niemals, diese Art von Abstraktion von der zugrunde liegenden Plattform aus Gründen der reinen Effizienz zu fordern. Ein praktikabler Ansatz wäre die Einführung einer separaten Kategorie von Zeigern/Referenzen wie "Zeiger/Referenz auf Bitfeld", die eine kompliziertere innere Struktur als ein gewöhnlicher Datenzeiger/Referenz hätte. Solche Typen wären aus gewöhnlichen Datenzeiger-/Referenztypen umsetzbar, aber nicht umgekehrt. Aber es scheint sich nicht zu lohnen. In praktischen Fällen, wenn ich mit Daten arbeiten muss, die in Bits und Bitsequenzen gepackt sind, bevorzuge ich es oft, Bitfelder manuell zu implementieren und Bitworte auf Sprachlevel zu vermeiden. Der Name des Bitfelds ist eine Kompilierzeit-Entität ohne Möglichkeit der Laufzeitauswahl irgendeiner Art. Wenn die Laufzeitauswahl erforderlich ist, besteht ein besserer Ansatz darin, ein normales uint32_t Datenfeld zu deklarieren und die einzelnen Bits und Gruppen von Bits darin manuell zu verwalten. Die Laufzeitauswahl eines solchen manuellen "Bitfelds" wird leicht durch Masken und Verschiebungen implementiert (beide können Laufzeitwerte sein). Dies liegt im Grunde an der manuellen Implementierung der oben genannten "Superpointer".

+0

Ich bezeichne dies als die Antwort, weil es deutlich macht, was ich für das Schlüsselargument halte: Wenn ein Verweis auf das Wort, das ein Bitfeld enthält, so funktionieren würde, wie ich skizziert habe, wären zusätzliche Informationen über die Offset des Bitfelds in das Wort, und das ist nicht praktisch implementierbar angesichts der Referenzen-sind-Zeiger-Under-the-Hood-Modell, das C++ verwendet. – KnowItAllWannabe

9

Sie können keine nicht-const Verweis auf ein Bitfeld aus dem gleichen Grund nehmen Sie seine Adresse mit & nicht annehmen kann: seine tatsächliche Adresse nicht unbedingt ausgerichtet char, die definitions die kleinste adressierbare Einheit des Speichers ist in der abstrakten C++ - Maschine. Sie können einen const Verweis darauf nehmen, weil der Compiler den Wert freigibt, da er nicht mutiert wird.

Betrachten Sie das Problem der separaten Kompilierung. Eine Funktion, die einen const uint32_t& übernimmt, muss den gleichen Code verwenden, um auf jedem const uint32_t& zu arbeiten. Wenn für normale Werte und Bitfeldwerte ein anderes Schreibverhalten erforderlich ist, codiert der Typ nicht genügend Informationen, damit die Funktion auf beiden korrekt funktioniert.

+0

Dies beantwortet nicht wirklich die Frage, IMO. Warum kann die Nicht-'const'-Referenz nicht an das Wort gebunden werden, das das Bitfeld enthält, und dann bei einem Schreibvorgang die notwendige Maskierung durchführen, um nur die Bits im Bitfeld zu modifizieren? Das passiert vermutlich, wenn das Bitfeld direkt modifiziert wird, nein? – KnowItAllWannabe

+1

Das letzte Bit ist falsch. Ein const-Verweis impliziert nichts darüber, dass der Wert mutiert ist oder nicht. Es verhindert nur Mutationen durch * diese * Referenz. –

+1

@AndreyT Das macht diese Formulierung noch immer nicht korrekt. Der Compiler ist nicht frei, ihn zu kopieren, weil er nicht mutiert wird. Es gibt hier keine Ursache. Der Compiler kann es per Dekret * kopieren. Es liegt nicht an der Veränderlichkeit des Bitfeldes, sondern daran, dass es so geschrieben ist. Eigentlich ist es nicht wirklich * kostenlos *, es zu kopieren, es ist * erforderlich * es zu kopieren. –