C ++ 20 introduce std::common_reference. Qual è il suo scopo? Qualcuno può fare un esempio di come usarlo?
C ++ 20 introduce std::common_reference. Qual è il suo scopo? Qualcuno può fare un esempio di come usarlo?
Risposte:
common_reference è venuto fuori dai miei sforzi per elaborare una concettualizzazione degli iteratori di STL che si adatta agli iteratori proxy.
Nella STL, gli iteratori hanno due tipi associati di particolare interesse: referencee value_type. Il primo è il tipo di ritorno dell'iteratore operator*e il value_typeè il tipo (non costante, non di riferimento) degli elementi della sequenza.
Gli algoritmi generici spesso hanno bisogno di fare cose del genere:
value_type tmp = *it;
... quindi sappiamo che ci deve essere qualche relazione tra questi due tipi. Per gli iteratori non proxy la relazione è semplice: referenceè sempre value_type, facoltativamente const e qualificato di riferimento. I primi tentativi di definire il InputIteratorconcetto richiedevano che l'espressione *itfosse convertibile in const value_type &, e per la maggior parte degli iteratori interessanti è sufficiente.
Volevo che gli iteratori in C ++ 20 fossero più potenti di così. Ad esempio, considera le esigenze di a zip_iteratorche scorre due sequenze in fase di blocco. Quando fai la dereferenza a zip_iterator, ottieni un temporaneo pairdei due referencetipi di iteratori . Quindi, ziping vector<int>e a vector<double>avrebbero questi tipi associati:
zipiteratore reference: pair<int &, double &>
zipiteratore value_type:pair<int, double>
Come puoi vedere, questi due tipi non sono correlati tra loro semplicemente aggiungendo la qualifica di cv e ref di alto livello. Eppure lasciare che i due tipi siano arbitrariamente diversi sembra sbagliato. Chiaramente c'è qualche relazione qui. Ma qual è la relazione e cosa possono assumere in modo sicuro gli algoritmi generici che operano su iteratori sui due tipi?
La risposta in C ++ 20 è che per qualsiasi tipo di iteratore valido, proxy o no, i tipi reference &&e value_type &condividono un riferimento comune . In altre parole, per alcuni iteratori itesiste un tipo CRche rende ben formato il seguente formato:
void foo(CR) // CR is the common reference for iterator I
{}
void algo( I it, iter_value_t<I> val )
{
foo(val); // OK, lvalue to value_type convertible to CR
foo(*it); // OK, reference convertible to CR
}
CRè il riferimento comune. Tutti gli algoritmi possono fare affidamento sul fatto che questo tipo esiste e possono essere utilizzati std::common_referenceper calcolarlo.
Quindi, questo è il ruolo che common_referencesvolge nell'STL in C ++ 20. Generalmente, a meno che non si stiano scrivendo algoritmi generici o iteratori proxy, è possibile ignorarlo in sicurezza. È lì sotto le coperte per garantire che i tuoi iteratori rispettino i loro obblighi contrattuali.
EDIT: L'OP ha anche chiesto un esempio. Questo è un po 'inventato, ma immagina che sia C ++ 20 e ti viene dato un intervallo rdi tipo di accesso casuale di Rcui non sai nulla e vuoi sortl'intervallo.
Immagina inoltre che, per qualche motivo, desideri utilizzare una funzione di confronto monomorfo, come std::less<T>. (Forse hai cancellato il tipo dell'intervallo e devi anche cancellare il tipo della funzione di confronto e passarla attraverso un virtual? Ancora una volta.) Cosa dovrebbe Tessere std::less<T>? Per quello useresti common_reference, o l'helper iter_common_reference_tche è implementato in termini di esso.
using CR = std::iter_common_reference_t<std::ranges::iterator_t<R>>;
std::ranges::sort(r, std::less<CR>{});
Ciò è garantito per funzionare, anche se range rha iteratori proxy.
pair<T&,U&>e pair<T,U>&avrebbe un riferimento comune, e sarebbe semplicemente pair<T&,U&>. Tuttavia, poiché std::pairnon vi è alcuna conversione da pair<T,U>&a pair<T&,U&>anche se una tale conversione è valida in linea di principio. (Questo, per inciso, è il motivo per cui non abbiamo una zipvisione in C ++ 20.)
pair, invece di un tipo che potrebbe essere specificamente progettato per il suo scopo , con opportune conversioni implicite, se necessario?
std::pair; farà qualsiasi tipo adatto a coppia con le conversioni appropriate, e range-v3 definisce tale tipo a coppia. In seno al Comitato, a LEWG non piaceva l'idea di aggiungere alla Standard Library un tipo che era quasi ma non del tutto std::pair, sia esso normativo o no, senza prima fare due diligence sui pro e contro del semplice std::pairlavoro.
tuple, pair, tomato, to- MAH- to. pairha questa bella funzione a cui puoi accedere agli elementi con .firste .second. I vincoli strutturati aiutano in parte all'imbarazzo di lavorare con tuples, ma non con tutti.