Perché il vettore di libc ++ <bool> :: const_reference non è bool?


92

La sezione 23.3.7 Classe vector<bool>[vector.bool], paragrafo 1 afferma:

template <class Allocator> class vector<bool, Allocator> {
public:
    // types:
    typedef bool              const_reference;
    ...

Tuttavia questo programma non riesce a compilare quando si utilizza libc ++:

#include <vector>
#include <type_traits>

int
main()
{
    static_assert(std::is_same<std::vector<bool>::const_reference, bool>{}, "?");
}

Inoltre noto che lo standard C ++ è stato coerente in questa specifica sin dal C ++ 98. E noto inoltre che libc ++ non ha seguito costantemente questa specifica dalla prima introduzione di libc ++.

Qual è la motivazione di questa non conformità?

Risposte:


99

La motivazione di questa estensione, che è rilevabile da un programma conforme, e quindi non conforme, è quella di far vector<bool>comportarsi più comevector<char> rispetto ai riferimenti (const e non).

introduzione

Dal 1998, vector<bool>è stato deriso come "non proprio un contenitore". LWG 96 , una delle primissime questioni LWG, ha lanciato il dibattito. Oggi, 17 anni dopo, vector<bool>rimane sostanzialmente invariato.

Questo documento entra in alcuni esempi specifici su come il comportamento di vector<bool>differisce da ogni altra istanziazione di vector, danneggiando così il codice generico. Tuttavia lo stesso articolo discute a lungo le proprietà di prestazione molto bellevector<bool> possono avere se implementate correttamente.

Riepilogo : vector<bool>non è un cattivo contenitore. In realtà è abbastanza utile. Ha solo un brutto nome.

Torna a const_reference

Come introdotto sopra e descritto in dettaglio qui , ciò che è negativo vector<bool>è che si comporta in modo diverso nel codice generico rispetto ad altre vectoristanze. Ecco un esempio concreto:

#include <cassert>
#include <vector>

template <class T>
void
test(std::vector<T>& v)
{
    using const_ref = typename std::vector<T>::const_reference;
    const std::vector<T>& cv = v;
    const_ref cr = cv[0];
    assert(cr == cv[0]);
    v[0] = 1;
    assert(true == cv[0]);
    assert(cr == cv[0]);  // Fires!
}

int
main()
{
    std::vector<char> vc(1);
    test(vc);
    std::vector<bool> vb(1);
    test(vb);
}

La specifica standard dice che l'assert contrassegnato // Fires!si attiverà, ma solo quando testviene eseguito con a vector<bool>. Quando viene eseguito con un vector<char>(o qualsiasi vectoraltro boolquando Tviene assegnato un valore non predefinito appropriato ), il test viene superato.

L'implementazione di libc ++ ha cercato di minimizzare gli effetti negativi di un vector<bool>comportamento diverso nel codice generico. Una cosa che ha fatto per ottenere ciò è stata creare vector<T>::const_referenceun riferimento proxy , proprio come specificato vector<T>::reference, tranne per il fatto che non è possibile assegnare tramite esso. Cioè, su libc ++, vector<T>::const_referenceè essenzialmente un puntatore al bit all'interno di vector, invece di una copia di quel bit.

Su libc ++ quanto sopra testpassa per entrambi vector<char>e vector<bool>.

A che costo?

Lo svantaggio è che questa estensione è rilevabile, come mostrato nella domanda. Tuttavia, pochissimi programmi si preoccupano effettivamente del tipo esatto di questo alias e più programmi si preoccupano del comportamento.

Qual è la motivazione di questa non conformità?

Per dare al client libc ++ un comportamento migliore nel codice generico, e forse dopo sufficienti test sul campo, proporre questa estensione a un futuro standard C ++ per il miglioramento dell'intero settore C ++.

Una tale proposta potrebbe presentarsi sotto forma di un nuovo contenitore (ad esempio bit_vector) che ha più o meno la stessa API di oggi vector<bool>, ma con alcuni aggiornamenti come quello const_referencediscusso qui. Seguito dalla deprecazione (ed eventuale rimozione) della vector<bool>specializzazione. bitsetpotrebbe anche utilizzare un piccolo aggiornamento in questo reparto, ad esempio add const_reference, e una serie di iteratori.

Cioè, col senno di poi bitsetè a vector<bool>(che dovrebbe essere rinominato in bit_vector- o qualsiasi altra cosa), così come arrayè vector. E l'analogia dovrebbe valere anche se non stiamo parlando boolcome value_typedi vectored array.

Ci sono diversi esempi di funzionalità di C ++ 11 e C ++ 14 che sono iniziate come estensioni in libc ++. È così che si evolvono gli standard. L'effettiva esperienza sul campo positiva dimostrata ha una forte influenza. La gente degli standard è un gruppo conservatore quando si tratta di modificare le specifiche esistenti (come dovrebbero essere). Indovinare, anche quando sei sicuro di indovinare correttamente, è una strategia rischiosa per sviluppare uno standard riconosciuto a livello internazionale.


1
Domanda: potrebbe / vorrebbe la recente bozza di proposta sugli iteratori proxy di @EricNiebler in qualche modo legittimare le estensioni libc ++ e mettere vector<bool>su una base più di prima classe?
TemplateRex

Nota: preferirei avere un vector_bool<Alloc>e un array_bool<N>per rappresentare le versioni compresse (inclusi gli iteratori proxy ad accesso casuale che iterano tutti i bit) di vector<bool, Alloc>e array<bool, N>. Tuttavia, bitset<N>(ed è cugino boost::dynamic_bitset<Alloc>) rappresentano un'astrazione diversa: vale a dire le versioni compresse di std::set<int>. Quindi mi piacerebbe avere, diciamo, bit_array<N>ed bit_vector<Alloc>essere il successore del franchise di bitset, con iteratori bidirezionali appropriati (iteranti su 1 bit, piuttosto che su tutti i bit). Cosa ne pensi di questo?
TemplateRex

5
La mia bozza di proposta sugli iteratori proxy creerebbe vector<bool>un contenitore ad accesso casuale conforme allo standard. Non sarebbe vector<bool>una buona idea. :-) Sono d'accordo con Howard. Avrebbe dovuto chiamarsi qualcos'altro.
Eric Niebler

1
Perché non c'è un modo per disattivare le estensioni di libc ++ e ottenere un comportamento strettamente conforme? (Non sto nemmeno chiedendo di rendere la conformità come predefinita, solo un modo per disabilitare le estensioni di libc ++ per poter scrivere codice portabile). Come sapete, in passato sono stato morso dalle estensioni di tuple di libc ++ e recentemente dall'estensione bitset :: const_reference.
gnzlbg

5
@gnzlbg: una quantità limitata di risorse economiche e temporali era disponibile per lo sviluppo iniziale di libc ++. Successivamente l'implementazione era destinata a non rendere felice ogni singolo utente. Date le risorse disponibili, sono stati fatti dei compromessi ingegneristici nel tentativo di massimizzare il numero di utenti soddisfatti, massimizzando i benefici per la comunità C ++ complessiva. Mi dispiace per la tua esperienza. Noto che le estensioni di tupla con cui ti sei imbattuto sono ora nel documento di lavoro corrente C ++ 1z. Su quel tema, ti sei sacrificato inconsapevolmente in modo che molti potessero beneficiarne, e quei molti ti hanno un debito di gratitudine.
Howard Hinnant
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.