2013-07-12 15 views
10

La sezione 9.6/3 in C++ 11 è insolitamente chiara: "Un riferimento non const non deve essere associato a un campo di bit". Qual è la motivazione dietro questo divieto?Perché i riferimenti non costanti ai bitfield sono proibiti?

Capisco che non è possibile associare direttamente un riferimento a un campo bit. Ma se dichiaro qualcosa del genere,

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

perché non posso dirlo?

IPv4Header h; 

auto& ecn = h.ECN; 

mi aspetto il codice sottostante per legare in realtà a tutta la std::uint32_t che contiene i bit che mi interessano, e mi aspetto lettura e scrittura per generare il codice per fare il mascheramento appropriata. Il risultato potrebbe essere grande e lento, ma mi sembra che dovrebbe funzionare. Ciò sarebbe coerente con il modo dire la standard che i riferimenti al const bitfields lavoro (sempre da 9,6/3):

Se l'inizializzatore per un riferimento di tipo const T & è un Ivalue che si riferisce a un bit -field, il riferimento è legato a un provvisorio inizializzato a mantenere il valore del campo-bit; il riferimento non è legato direttamente al campo di bit.

Questo suggerisce che scrivere su bitfield è il problema, ma non vedo cosa sia. Ho considerato la possibilità che il mascheramento necessario potesse introdurre razze in codice multithread, ma, per 1.7/3, campi di bit adiacenti di larghezza diversa da zero sono considerati un singolo oggetto ai fini del multithreading. Nell'esempio sopra, tutti i bitfield in un oggetto IPv4Header sarebbero considerati un singolo oggetto, pertanto il codice multithreading che tenta di modificare un campo durante la lettura di altri campi sarebbe, per definizione, già molto competitivo.

Mi manca chiaramente qualcosa. Che cos'è?

risposta

7

I riferimenti non const non possono essere associati a campi bit per lo stesso motivo per cui i puntatori non possono puntare a campi bit.

Mentre non è specificato se i riferimenti occupano la memoria, è chiaro che in casi non banali vengono implementati come puntatori sotto mentite spoglie e questa implementazione di riferimenti è "intesa" dagli autori della lingua. E proprio come i puntatori, i riferimenti devono puntare a un'unità di memoria indirizzabile. È impossibile associare un riferimento non const a un'unità di memoria che non è indirizzabile. Poiché i riferimenti non const richiedono il binding diretto, un riferimento non const non può essere associato a un campo bit.

L'unico modo per produrre un puntatore/riferimento che possa puntare a campi di bit sarebbe implementare una sorta di "superpointer" che oltre all'effettivo indirizzo in memoria conterrà anche una sorta di bit offset e bit -informazione di larghezza, per dire al codice di scrittura quali bit modificare. Si noti che queste informazioni aggiuntive dovrebbero essere presenti in tutti i tipi di puntatori di dati, poiché non esiste un tale tipo in C++ come "puntatore/riferimento al campo di bit". Questo è fondamentalmente equivalente all'implementazione di un modello di indirizzamento dello storage di livello superiore, abbastanza distaccato dal modello di indirizzamento fornito dalla piattaforma OS/hardware sottostante. Il linguaggio C++ non ha mai inteso richiedere quel tipo di astrazione dalla piattaforma sottostante per considerazioni di efficienza pura.

Un approccio praticabile sarebbe quello di introdurre una categoria separata di puntatori/riferimenti come "pointer/reference to bit-field", che avrebbe una struttura interna più complicata di un normale puntatore/riferimento di dati. Tali tipi sarebbero convertibili da normali puntatori di dati/tipi di riferimento, ma non viceversa. Ma non sembra valere la pena.

In casi pratici, quando devo gestire i dati compressi in bit e sequenze di bit, spesso preferisco implementare manualmente i campi di bit ed evitare campi di bit a livello di lingua. Il nome di bit-field è un'entità in fase di compilazione senza possibilità di selezione di runtime di alcun tipo. Quando è necessaria la selezione del run-time, un approccio migliore è quello di dichiarare un normale campo dati uint32_t e gestire manualmente i singoli bit e gruppi di bit all'interno di esso. La selezione del tempo di esecuzione di tale "campo di bit" manuale è facilmente implementabile tramite maschere e turni (entrambi possono essere valori di runtime). Fondamentalmente, questo è vicino all'implementazione manuale dei summenzionati "superpunti".

+0

La segnalo come risposta, perché rende esplicito quello che penso sia l'argomento chiave: se un riferimento alla parola che tiene un bitfield dovesse funzionare come ho abbozzato, ci sarebbe bisogno di ulteriori informazioni riguardo al offset del bitfield nella parola, e questo non è praticamente implementabile dato il modello di riferimento-i-pointers-under-the-hood che impiega C++. – KnowItAllWannabe

9

Non si può prendere un non const riferimento ad un campo di bit per la stessa ragione per cui non si può prendere il suo indirizzo con &: il suo indirizzo reale non è necessariamente allineata a char, che è per definizione la più piccola unità indirizzabile della memoria nella macchina astratta C++. È possibile prendere un riferimento a const perché il compilatore è libero di copia il valore, in quanto non verrà modificato.

Considerare il problema della compilazione separata. Una funzione che richiede un const uint32_t& deve utilizzare lo stesso codice per operare su qualsiasi const uint32_t&. Se è richiesto un comportamento di scrittura diverso per valori ordinari e valori di bitfield, il tipo non codifica informazioni sufficienti affinché la funzione funzioni correttamente su entrambi.

+0

Questo in realtà non risponde alla domanda, IMO. Perché il riferimento non-'nonstato 'non può legarsi alla parola contenente il bitfield, quindi, su una scrittura, eseguire il mascheramento necessario per modificare solo i bit nel campo bit? Questo è presumibilmente ciò che succede quando il bitfield viene modificato direttamente, no? – KnowItAllWannabe

+1

L'ultimo bit è sbagliato. Un riferimento const non implica nulla sul fatto che il valore sia o non sia mutato. Previene solo la mutazione attraverso * quel * riferimento. –

+1

@AndreyT Ciò non rende ancora la formulazione corretta. Il compilatore non è libero di copiarlo perché non sarà mutato. Non c'è causalità qui. Il compilatore è libero di copiarlo * per decreto *. Non è a causa della mutabilità del bitfield, è perché è scritto così. In realtà, non è veramente * gratuito * copiarlo, è * obbligatorio * copiarlo. –

Problemi correlati