Qual è lo scopo di C ++ 20 std :: common_reference?


Risposte:


46

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.


2
Forse sono denso, ma puoi chiarire qual è il riferimento comune nell'esempio coppia-zip?
Happydave,

4
Idealmente, 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.)
Eric Niebler

4
@EricNiebler: " Questo, per inciso, è il motivo per cui non abbiamo una vista zip in C ++ 20. " C'è qualche ragione per cui un iteratore zip dovrebbe usare pair, invece di un tipo che potrebbe essere specificamente progettato per il suo scopo , con opportune conversioni implicite, se necessario?
Nicol Bolas,

5
@Nicol Bolas Non è necessario utilizzare 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.
Eric Niebler,

3
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.
Eric Niebler,
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.