Какова цель C ++ 20 std :: common_reference?

Ответы:

46

common_reference Я пришел к тому, что придумал концептуализацию итераторов STL, которая включает прокси-итераторы.

В STL итераторы имеют два связанных типа, представляющих особый интерес: referenceи value_type. Первый тип является возвращаемым типом итератора operator*, а тип value_type(неконстантный, не ссылочный) элементов последовательности.

Универсальные алгоритмы часто нуждаются в таких вещах:

value_type tmp = *it;

... поэтому мы знаем, что между этими двумя типами должна быть какая-то связь. Для не-прокси-итераторов связь проста: referenceвсегда value_type, необязательно, const и ссылка квалифицирована. Ранние попытки определить InputIteratorконцепцию требовали, чтобы выражение *itбыло конвертируемым const value_type &, а для наиболее интересных итераторов этого достаточно.

Я хотел, чтобы итераторы в C ++ 20 были более мощными, чем эта. Например, рассмотрим потребности в zip_iteratorитерации двух последовательностей в шаге блокировки. Когда вы разыменовываете a zip_iterator, вы получаете временный тип pairдвух итераторов reference. Таким образом, zip«ИНГ vector<int>и vector<double>будет иметь эти связанные типы:

zipитератор reference: pair<int &, double &>
zipитератор value_type:pair<int, double>

Как видите, эти два типа не связаны друг с другом, просто добавляя квалификацию cv- и ref верхнего уровня. И все же допускать, что эти два типа произвольно различаются, кажется неправильным. Очевидно, здесь есть некоторые отношения. Но какова связь, и что могут предположить общие алгоритмы, которые работают на итераторах, о двух типах?

Ответ на C ++ 20 является то , что для любого действительного типа итератора, прокси или нет, типы reference &&и value_type &разделяют общие ссылки . Другими словами, для некоторого итератора itесть некоторый тип, CRкоторый делает следующее правильно сформированным:

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это общая ссылка. Все алгоритмы могут опираться на тот факт, что этот тип существует, и могут использовать его std::common_referenceдля вычисления.

Так вот, какую роль common_referenceиграет STL в C ++ 20. Как правило, если вы не пишете универсальные алгоритмы или прокси-итераторы, вы можете спокойно их игнорировать. Он находится под прикрытием, гарантируя, что ваши итераторы выполняют свои договорные обязательства.


РЕДАКТИРОВАТЬ: ОП также попросил пример. Это немного надумано, но представьте, что это C ++ 20, и вам дан диапазон произвольного доступа rтипа, Rо котором вы ничего не знаете, и вы хотите получить sortдиапазон.

Далее представьте, что по какой-то причине вы хотите использовать мономорфную функцию сравнения, например std::less<T>. (Может быть, вы стерли диапазон, и вам нужно также стереть функцию сравнения и пропустить ее через virtual? Снова отрезок.) Что должно Tбыть в std::less<T>? Для этого вы бы использовали common_reference, или помощник, iter_common_reference_tкоторый реализован в терминах этого.

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

Это гарантированно сработает, даже если у диапазона rесть прокси-итераторы.

Эрик Ниблер
источник
2
Может быть, я дремучий, но не могли бы вы уточнить, что общего в примере с zip-парой?
Happyydave
4
В идеале pair<T&,U&>и pair<T,U>&будет иметь общую ссылку, и это будет просто pair<T&,U&>. Однако, потому std::pairчто нет преобразования из pair<T,U>&в, pair<T&,U&>хотя такое преобразование в принципе звучит. (Это, кстати, поэтому у нас нет zipпредставления в C ++ 20.)
Эрик Ниблер
4
@EricNiebler: «Кстати , именно поэтому у нас нет представления zip в C ++ 20». Есть ли какая-то причина, по которой пришлось бы использовать итератор zip pairвместо типа, специально разработанного для этой цели? с соответствующими неявными преобразованиями по мере необходимости?
Никол Болас
5
@Nolol Bolas Нет необходимости использовать std::pair; подойдет любой подходящий парный тип с соответствующими преобразованиями, а range-v3 определяет такой парный тип. В Комитете LEWG не понравилась идея добавить в Стандартную библиотеку тип, который был бы почти, но не совсем std::pair, будь то нормативным или нет, без предварительного тщательного изучения плюсов и минусов простого выполнения std::pairработы.
Эрик Ниблер
3
tuple, pair- tomato, to- MAH- to. pairимеет эту приятную особенность, что вы можете получить доступ к элементам с помощью .firstи .second. Структурированные привязки помогают с некоторыми неудобствами работы с tuples, но не со всеми.
Эрик Ниблер