Jaki jest cel C ++ 20 std :: common_reference?

Odpowiedzi:

46

common_reference wyszedł z moich wysiłków, aby wymyślić koncepcję iteratorów STL, która obsługuje iteratory proxy.

W STL iteratory mają dwa powiązane typy szczególnego zainteresowania: reference i value_type. Pierwszy z nich to typ zwracany przez iterator operator*, a value_typejest to (nie stały, brak odniesienia) elementów sekwencji.

Ogólne algorytmy często muszą robić takie rzeczy:

value_type tmp = *it;

... więc wiemy, że musi istnieć pewien związek między tymi dwoma typami. W przypadku iteratorów innych niż proxy związek jest prosty: referencejest zawsze value_type, opcjonalnie const i referencja kwalifikowana. Wczesne próby zdefiniowania InputIteratorkoncepcji wymagały *itkonwersji tego wyrażenia const value_type &, a dla najbardziej interesujących iteratorów było to wystarczające.

Chciałem, aby iteratory w C ++ 20 były bardziej wydajne. Rozważmy na przykład potrzebę zip_iteratoriteracji dwóch sekwencji w kroku blokowania. Gdy odrzucisz a zip_iterator, otrzymasz tymczasowy typ pairdwóch iteratorów reference. Więc, zipingvector<int> i a vector<double>miałyby następujące powiązane typy:

zipiterator's reference:pair<int &, double &>
zip iterator's value_type:pair<int, double>

Jak widać, te dwa typy nie są ze sobą powiązane po prostu poprzez dodanie najwyższej klasy kwalifikacji cv i ref. A jednak pozwolenie, by te dwa typy były dowolnie różne, wydaje się złe. Oczywiście jest kilka tutaj związek. Ale jaka jest relacja i co ogólne algorytmy działające na iteratorach mogą bezpiecznie założyć o tych dwóch typach?

Odpowiedź w C ++ 20 jest taka, że ​​dla każdego poprawnego typu iteratora, proxy lub nie, typy reference &&i value_type &współużytkowane są wspólne odwołania . Innymi słowy, dla niektórych iteratorów itistnieje jakiś typ, CRktóry sprawia, że ​​następujący poprawnie sformułowany:

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
}

CRjest wspólnym odniesieniem. Wszystkie algorytmy mogą polegać na fakcie, że ten typ istnieje i można z niego korzystaćstd::common_reference do jego obliczenia.

Taką rolę common_referencepełni STL w C ++ 20. Zasadniczo, chyba że piszesz ogólne algorytmy lub iteratory proxy, możesz go bezpiecznie zignorować. Jest pod osłonami, zapewniając, że Twoje iteratory wypełniają swoje zobowiązania umowne.


EDYCJA: PO poprosił również o przykład. Jest to trochę wymyślone, ale wyobraź sobie, że jest to C ++ 20, a otrzymasz zakres rtypu dostępu o swobodnym dostępie, Ro którym nic nie wiesz, i chcesz do sorttego zakresu.

Ponadto wyobraź sobie, że z jakiegoś powodu chcesz użyć monomorficznej funkcji porównawczej, takiej jak std::less<T>. (Może skasowałeś zakres i musisz także skasować funkcję porównania i przekazać ją przez virtual? Ponownie, odcinek.) Co powinno Tbyć std::less<T>? Do tego użyłbyś common_referencelub pomocnika, iter_common_reference_tktóry jest zaimplementowany pod tym względem.

using CR = std::iter_common_reference_t<std::ranges::iterator_t<R>>;
std::ranges::sort(r, std::less<CR>{});

Gwarantuje to, że zadziała, nawet jeśli zasięg rma iteratory proxy.

Eric Niebler
źródło
2
Może jestem gęsty, ale czy możesz wyjaśnić, jakie jest wspólne odniesienie w przykładzie pary zip?
happydave
4
Idealnie, pair<T&,U&>i pair<T,U>&miałaby wspólny punkt odniesienia, a byłoby po prostu pair<T&,U&>. Jednak std::pairnie ma konwersji z pair<T,U>&na, pair<T&,U&>nawet jeśli taka konwersja jest w zasadzie rozsądna. (Nawiasem mówiąc, dlatego nie mamy zipwidoku w C ++ 20).
Eric Niebler
4
@EricNiebler: „ Nawiasem mówiąc, dlatego nie mamy widoku zip w C ++ 20. ” Czy istnieje jakiś powód, dla którego należy użyć iteratora zip pairzamiast typu, który mógłby być specjalnie zaprojektowany do jego celu , w razie potrzeby z odpowiednimi niejawnymi konwersjami?
Nicol Bolas,
5
@Nicol Bolas Nie trzeba używać std::pair; zrobi to dowolny odpowiedni typ podobny do pary z odpowiednimi konwersjami, a zakres v3 definiuje taki typ podobny do pary. W Komitecie LEWG nie podobał się pomysł dodania do Biblioteki Standardowej typu, który był prawie, ale nie całkiem std::pair, normatywny czy nie, bez uprzedniej staranności w kwestii zalet i wad zwykłego wykonywania std::pairpracy.
Eric Niebler,
3
tuple, pair, tomato, to- MAH- to. pairma tę fajną funkcję, dzięki której można uzyskać dostęp do elementów za pomocą .firsti .second. Wiązania strukturalne pomagają w niektórych niezręcznościach pracy z tuples, ale nie ze wszystkimi.
Eric Niebler