понимание того, как работает zip в range-v3 - PullRequest
0 голосов
/ 21 апреля 2020

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

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 работают нормально, но в некоторых случаях это не так. Может кто-нибудь дать объяснение по этому поводу?

Ответы [ 2 ]

1 голос
/ 22 апреля 2020

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

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 является разреженной. Чтобы найти такую ​​информацию, как приведенная выше, мы, конечно, ищем источник для range-v3, быстрые эксперименты на godbolt.org (range-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));
0 голосов
/ 24 апреля 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/

...