Как с диапазонами Range v3 объединить представления и действия в единый конвейер?

Я изучаю диапазоны C++20 (используя Range-V3-VS2015). У меня есть этот код, который отлично работает:

string clean;
auto tmp1 = input | view::remove_if(not_alpha) | view::transform(::tolower);
std::copy(tmp1.begin(), tmp1.end(), std::back_inserter(clean));
auto tmp2 = clean |= action::sort |  action::unique;

Однако я хотел бы объединить два конвейера, определяющих tmp1 и tmp2, в один конвейер. Это возможно? Я пробовал множество вещей, включая добавление view::move и view::copy посередине, но безрезультатно.


person busfahrer    schedule 16.05.2019    source источник
comment
Вы не должны брать адрес функций стандартной библиотеки больше; используйте лямбда, чтобы вызвать их. (Да, это немного глупо для библиотечных функций C, которые C++ не перегружает.)   -  person Davis Herring    schedule 16.05.2019
comment
Почему вы хотите объединить их в один конвейер? А не должно ли быть наоборот — разбить эти два пайплайна на отдельные операции?   -  person user7860670    schedule 16.05.2019


Ответы (2)


Да, ты можешь. Вам нужно использовать преобразование, чтобы материализовать представление в реальный контейнер для выполнения над ним действий. Я нашел новый фрагмент кода в основной ветке range-v3, представляющий range::v3::to<Container> для выполнения таких преобразований.

git blame предполагает, что Эрик начал работать над ним в этом году (2019), и он еще не задокументирован. Тем не менее, я нахожу range-v3/test довольно хорошим учебным материалом о том, как использовать библиотеку :)

Сомневаюсь, что он доступен в ветке VS2015. Однако Visual 2017 уже умеет брать главную ветку библиотеки.

#include <string>
#include <iostream>
#include <cctype>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/action/sort.hpp>
#include <range/v3/action/unique.hpp>
#include <range/v3/range/conversion.hpp>

int main() {
    using namespace ranges::v3;
    std::string input = " 1a2a3Z4b5Z6cz ";
    std::string result = input
                       | view::filter(::isalpha)
                       | view::transform(::tolower)
                       | to<std::string>
                       | action::sort
                       | action::unique;
    std::cout << result << std::endl;
    return 0;
}

Выходы:

abcz

что я считаю, что вы ожидаете

person CygnusX1    schedule 16.05.2019
comment
Пришел сюда, чтобы сказать это. +1. - person Eric Niebler; 16.05.2019
comment
Спасибо! В качестве фона и для моего более глубокого понимания, почему это не работает ни с view::copy, ни с view::move? - person busfahrer; 16.05.2019

ranges::to — это то, что вам нужно.

Прокатить собственную полузамену несложно.

template<class C, class R>
C to_container( R&& r ) {
  using std::begin; using std::end;
  return C( begin(std::forward<R>(r)), end(std::forward<R>(r)) );
}

Не библиотечная сила (отсутствует ранний сбой как самая большая проблема и не поддерживается |), но вполне пригодный для использования.

а потом мы просто:

std::string r = to_container<std::string>( input | view::remove_if(not_alpha) | view::transform(::tolower) ) | action::sort |  action::unique;

Обратите внимание, что использование адресов функций в std больше не рекомендуется. (через @DavisHerring в комментарии выше)

Чтобы перейти на |:

template<class C>
struct to_container_t {
  template<class R>
  C operator()( R&& r )const {
    using std::begin; using std::end;
    return C( begin(std::forward<R>(r)), end(std::forward<R>(r)) );
  }
  template<class R>
  friend C operator|( R&& r, to_container_t self ){
    return self( std::forward<R>(r) );
  }
};
template<class C>
constexpr to_container_t<C> to_container{};

Что дает нам:

std::string r = input | view::remove_if(not_alpha) | view::transform(::tolower) | to_container<std::string> | action::sort |  action::unique;

Как требуется.

person Yakk - Adam Nevraumont    schedule 16.05.2019