Perché l’algoritmo standard C ++ “conta” restituisce un tipo differenza invece di size_t?

Perché è il tipo restituito di std::count il tipo_rete degli iteratori (spesso un ptrdiff_t ).

Poiché il conteggio non può mai essere negativo, non è dimension_t tecnicamente la scelta giusta? E se il conteggio supera l’intervallo di ptrdiff_t dato che la dimensione teorica ansible di un array può essere size_t ?


EDIT: Finora non c’è una risposta adeguata al motivo per cui la funzione restituisce ptrdiff_t . Alcune spiegazioni raccolte dalle risposte sottostanti sono che il tipo restituito è iterator_traits::difference_type che è generico e può essere qualsiasi cosa. Fino a quel momento ha senso. Ci sono casi in cui il conteggio può superare size_t . Tuttavia, non ha ancora senso perché il tipo restituito sia typedef ptrdiff_t iterator_traits::difference_type per gli iteratori standard invece di typedef size_t iterator_traits::difference_type .

L’algoritmo std::count() si basa sul tipo iteratore per definire un tipo integrale abbastanza grande da rappresentare qualsiasi dimensione di un intervallo. La ansible implementazione di contenitori include file e flussi di rete, ecc. Non è garantito che l’intera gamma si adatti allo spazio di indirizzamento del processo in una sola volta, quindi std::size_t potrebbe essere troppo piccolo.

L’unico tipo integrale offerto dallo standard std::iterator_traits<> è std::iterator_traits<>::difference_type , che è adatto per rappresentare “distanze” tra due iteratori. Per gli iteratori implementati come puntatori (wrappers of), questo tipo è std::ptrdiff_t . Non ci sono size_type o simili da tratti iteratori, quindi non c’è altra scelta.

size_t non è tecnicamente la scelta corretta, dal momento che potrebbe non essere abbastanza grande. Gli iteratori sono autorizzati ad eseguire iterazioni su “qualcosa” che è più grande di qualsiasi object in memoria, ad esempio un file su disco. Quando lo fanno, l’iteratore può definire un tipo più grande di size_t come suo size_t , se disponibile.

difference_type deve essere firmato perché in contesti diversi da std::count rappresenta offset tra iteratori in entrambe le direzioni . Per gli iteratori ad accesso casuale, it + difference è un’operazione perfettamente ragionevole anche quando la difference è negativa.

iterator_traits non offre un tipo senza segno. Forse dovrebbe, ma dato che non iterator_traits::difference_type è il miglior tipo disponibile.

La questione se gli iteratori dovrebbero offrire un tipo senza segno probabilmente si riferisce a un enorme conflitto di stili di codifica, indipendentemente dal fatto che i tipi non firmati debbano essere usati per i conteggi. Non propongo di riprodurre questo argomento qui, puoi cercarlo. ptrdiff_t ha un debole che su alcuni sistemi non può rappresentare tutte le differenze di puntatore valide, e quindi anche non può rappresentare tutti i risultati attesi di std::count .

Per quanto ne so, anche in C ++ 03 lo standard lo proibiva, forse per caso. 5.7 / 6 parla della sottrazione che potrebbe traboccare ptrdiff_t , proprio come fa C. Ma la tabella 32 (requisiti degli allocatori) dice che X::difference_type può rappresentare la differenza tra due puntatori, e std::allocator è garantito per usare ptrdiff_t come suo tipo_versi (20.1.5 / 4). C ++ 11 è simile. Quindi una parte dello standard pensa che la sottrazione del puntatore possa traboccare ptrdiff_t , e un’altra parte dello standard dice che non può farlo.

std::count presumibilmente è stato progettato sotto la stessa ipotesi (possibilmente difettosa) come requisiti di allocatore, che ptrdiff_t è abbastanza grande per esprimere la dimensione di qualsiasi object e (in generale) un tipo_limitato di un iteratore può esprimere il conteggio di iterands tra due iteratori .

Il tipo restituito è typename iterator_traits::difference_type che in questo caso particolare risulta essere ptrdiff_t .

Presumibilmente difference_type stato selezionato perché il numero massimo di elementi corrispondenti nell’intervallo sarebbe la differenza di iteratore last - first .

Anche se un conteggio non può essere negativo, il tipo di ritorno è specificato come iterator_traits::difference_type e la differenza tra due iteratori può essere negativa.

Originariamente std::count era:

 template  void count(InputIterator first, InputIterator last, const EqualityComparable& value, Size& n); 

In quella funzione, la Size è un parametro del modello. Può essere quello che vuoi, ed è tua responsabilità assicurarti che sia corretto. Potrebbe essere il tipo più lungo sulla tua piattaforma.

Il mio sospetto è che quando la nuova forma:

 template  iterator_traits::difference_type count(InputIterator first, InputIterator last, const EqualityComparable& value); 

è stato aggiunto iterator_traits esisteva già, quindi riutilizzare il tipo esistente ha avuto il vantaggio di mantenere le modifiche allo standard piccolo e localizzato, rispetto all’aggiunta di un altro typedef in iterator_traits .

Facendolo in questo modo, usando iterator_traits anziché usare semplicemente std::size_type significa che ogni ansible iteratore ottiene l’opzione per specificare esattamente quale tipo deve essere restituito da std::count . Ciò include iteratori personalizzati che leggono da una rete, o un disco, che può usare qualcosa di molto più grande di ptrdiff_t o size_type e amici. ( Potrebbe essere una sorta di “BigInt” se necessario). Significa anche che l’utente non è responsabile della deduzione del tipo appropriato da utilizzare, il che può essere complicato, proprio a causa della possibilità di iteratore personalizzato.

Se l’iteratore fosse una matrice, implicherebbe che il risultato si trovi all’interno della gamma dell’array.

Per questo specifico algoritmo non riesco a pensare a una ragione che sia interessante. Per qualcuno che usa questo come componente può essere interessante, però.

La pagina dice che farebbe qualcosa di equivalente . Quindi per il caso di un array può fare qualcosa come una differenza puntatore diretto. Questa sarebbe una specializzazione piuttosto veloce se fosse applicabile.

difference_type solito denota il tipo adatto a denotare una distanza in un array o simile. Il seguente testo deriva dai requisiti degli allocatori, ma ogni volta che lo standard parla di difference_type significa lo stesso concetto:

un tipo che può rappresentare la differenza tra due punti qualsiasi nel modello di allocazione

Il tipo naturale per questo è ptrdiff_t.

Per il size_type si dice:

un tipo che può rappresentare la dimensione dell’object più grande nel modello di allocazione.

Il tipo naturale qui è size_t.

Ora per il conteggio di qualsiasi elemento in un intervallo (o un array) è necessario almeno il tipo adatto a specificare la differenza last-first . Sembra molto naturale sceglierne uno.