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: reference
e 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 InputIterator
concetto richiedevano che l'espressione *it
fosse 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_iterator
che scorre due sequenze in fase di blocco. Quando fai la dereferenza a zip_iterator
, ottieni un temporaneo pair
dei due reference
tipi di iteratori . Quindi, zip
ing vector<int>
e a vector<double>
avrebbero questi tipi associati:
zip
iteratore reference
: pair<int &, double &>
zip
iteratore 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 it
esiste un tipo CR
che 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_reference
per calcolarlo.
Quindi, questo è il ruolo che common_reference
svolge 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 r
di tipo di accesso casuale di R
cui non sai nulla e vuoi sort
l'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 T
essere std::less<T>
? Per quello useresti common_reference
, o l'helper iter_common_reference_t
che è 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 r
ha iteratori proxy.
pair<T&,U&>
e pair<T,U>&
avrebbe un riferimento comune, e sarebbe semplicemente pair<T&,U&>
. Tuttavia, poiché std::pair
non 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 zip
visione 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::pair
lavoro.
tuple
, pair
, tomato
, to
- MAH
- to
. pair
ha questa bella funzione a cui puoi accedere agli elementi con .first
e .second
. I vincoli strutturati aiutano in parte all'imbarazzo di lavorare con tuple
s, ma non con tutti.