понимание того, как работает zip в диапазоне v3

Я пытаюсь понять, как range :: views :: zip работает в range-v3. Я понимаю, что это диапазон, который позволяет выполнять итерацию по нескольким диапазонам в одном цикле, создавая кортеж из элементов в разных диапазонах.

std::vector<int> v1 = {0, 1, 2};
std::vector<char> v2 = {'a', 'b', 'c'};


auto zip = ranges::views::zip(v1,v2);
// zip(v1,v2) = [(0,a), (1,b), (2,c)]

ranges::actions::sort(zip);
std::sort(std::begin(zip), std::end(zip));

Сортировка с использованием ranges::actions работает нормально, но std::sort не компилируется и выдает следующую ошибку

/usr/include/c++/9.3.0/bits/stl_algobase.h:151: error: no matching function for call to ‘swap(concepts::return_t<ranges::common_pair<int&, double&>, void>, concepts::return_t<ranges::common_pair<int&, double&>, void>)’
  151 |       swap(*__a, *__b);
      |       ~~~~^~~~~~~~~~~~

Почему это происходит?

Я также попытался удалить элементы в обоих контейнерах одновременно. ranges::actions::unique не компилируется со следующей ошибкой:

/home/jjcasmar/projects/cpfsofaplugin/src/CPFSofaPlugin/minimalExample.cpp:27: error: no match for call to ‘(const ranges::actions::action_closure<ranges::actions::unique_fn>) (ranges::zip_view<ranges::ref_view<std::vector<int, std::allocator<int> > >, ranges::ref_view<std::vector<double, std::allocator<double> > > >&)’
   27 |     ranges::actions::unique(v1Andv2);
      |                                    ^

но auto lastIt = std::unique(std::begin(v1Andv2), std::end(v1Andv2)) компилирует find, хотя я не знаю, как заставить внутренние итераторы zip-архива стирать последние элементы.

Я действительно не понимаю, как это работает под капотом и почему в некоторых случаях алгоритмы std работают нормально, но в некоторых случаях это не так. Может кто-нибудь дать объяснение по этому поводу?


person jjcasmar    schedule 21.04.2020    source источник


Ответы (2)


Посмотрите на типы:

auto zip = ranges::views::zip(v1, v2);
// ranges::zip_view<
//   ranges::ref_view<std::vector<int>>
//   ranges::ref_view<std::vector<char>>
// >

auto begin = std::begin(zip);
// ranges::basic_iterator<
//   ranges::iter_zip_with_view<
//     ranges::detail::indirect_zip_fn_,
//     ranges::ref_view<std::vector<int>>,
//     ranges::ref_view<std::vector<char>>
//   >::cursor<false>
// >

using traits = std::iterator_traits<decltype(begin)>;
static_assert(std::is_same_v<traits::value_type, std::pair<int, char>>);
static_assert(std::is_same_v<traits::reference, ranges::common_pair<int&, char&>>);

Тип value_type - это std::pair значений. Тип reference - это ranges::common_pair ссылок.

std::sort использует std::iter_swap, который указан в терминах разыменования итераторов и вызова std::swap. Итак, std::sort попытается поменять местами две ranges::common_pair ссылок. С другой стороны, ranges::actions::sort использует ranges::iter_swap, который настроен для обработки пар и кортежей ссылок.

Пары и кортежи ссылок являются / были гражданами второго сорта в стандартной библиотеке.

ranges::actions::unique требуется стираемый диапазон, который, очевидно, не удовлетворяет.

Добавлено

Документация для range-v3 немногочисленна. Чтобы найти информацию, подобную приведенной выше, конечно же, посмотрите источник для диапазона-v3, быстрые эксперименты на godbolt.org (диапазон-v3 - доступная библиотека) и "стандартные" уловки C ++, чтобы найти тип переменной. (например, вызов шаблона функции, который объявлен, но не определен, с типом переменной в качестве аргумента шаблона, и просмотр того, какой экземпляр вызывается).

Чтобы прокомментировать unique, ranges::action::unique не возвращает итератор. Он стирает неуникальные элементы и возвращает диапазон (см. источник). В части ошибки компилятора, которая была опущена, ошибка ссылается на тот факт, что диапазон не подлежит стиранию (погребен в огромной ошибке).

ranges::unique возвращает итератор и может быть вызван без ошибок. Это basic_iterator<...>. Один из вариантов - использовать ranges::distance, чтобы найти расстояние от begin итератора zip, и использовать это для получения нижележащего итератора:

auto zip_uniq_iter = ranges::unique(zip);
auto first_uniq_iter = std::next(v1.begin(), ranges::distance(ranges::begin(zip), zip_uniq_iter));
person Jeff Garrett    schedule 21.04.2020
comment
Я понимаю, что не могу стирать элементы из представления. Но я могу использовать std :: unique, который возвращает итератор. Если я использую его с zip-представлением, я получаю итератор, который, как мне кажется, внутри должен иметь итераторы для диапазонов v1 и v2, не так ли? Как я могу их получить? Наконец, как мне найти эту информацию, которую вы предоставили? Документация range-v3 выглядит действительно странно - person jjcasmar; 22.04.2020

Вы не можете использовать std :: sort для представлений. Но вы можете преобразовать свой вид в вектор, и тогда он заработает: https://godbolt.org/z/_FvCdD < / а>

Для получения дополнительной информации о диапазонах я могу порекомендовать следующие сайты:

https://www.walletfox.com/course/quickref_range_v3.php https://mariusbancila.ro/blog/2019/01/20/cpp-code-samples-before-and-after-ranges/

person Porsche9II    schedule 23.04.2020
comment
Это решение требует двойной памяти. Диапазоны обеспечивают сортировку просмотров - person jjcasmar; 24.04.2020
comment
Ой, конечно, вы можете использовать range :: actions :: sort (zip). Но вы спросили, почему std :: sort не компилируется ... - person Porsche9II; 24.04.2020