Почему я не могу отменить разделенный диапазон с помощью range-v3?

Я хочу разделить, перевернуть, а затем соединить строку, используя range-v3. Однако приведенный ниже код не будет компилироваться.

#include <range/v3/all.hpp>
#include <iostream>

using namespace ranges;

int main(int argc, char *argv[])
{
    auto str = std::string("abc.def.ghi");
    auto sv = str
              | view::split('.')
              | view::reverse
              | view::join('.');
    std::cout<<sv;
    return 0;
}

Вывод компилятора:

error: invalid operands to binary expression ('decltype(pipeable_access::impl<view<reverse_fn> >::pipe(static_cast<ranges::v3::split_view<ranges::v3::iterator_range<std::_String_iterator<std::_String_val<std::_Simple_types<char> > >, std::_String_iterator<std::_String_val<std::_Simple_types<char> > > >, ranges::v3::view::split_fn::element_pred<std::basic_string<char, std::char_traits<char>, std::allocator<char> > &> > &&>(arg), pipe))' (aka 'void') and 'decltype(make_view(view_access::impl<join_fn>::bind(this->view_, static_cast<char &&>(ts))))' (aka 'view<ranges::v3::detail::pipeable_binder<std::_Binder<std::_Unforced, ranges::v3::view::join_fn &, const std::_Ph<1> &, char> > >'))
range\v3\view\any_view.hpp:60: candidate function not viable: cannot convert argument of incomplete type 'decltype(pipeable_access::impl<view<reverse_fn> >::pipe(static_cast<ranges::v3::split_view<ranges::v3::iterator_range<std::_String_iterator<std::_String_val<std::_Simple_types<char> > >, std::_String_iterator<std::_String_val<std::_Simple_types<char> > > >, ranges::v3::view::split_fn::element_pred<std::basic_string<char, std::char_traits<char>, std::allocator<char> > &> > &&>(arg), pipe))' (aka 'void') to 'ranges::v3::category' for 1st argument
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.21.27702\include\regex:1219: candidate function not viable: cannot convert argument of incomplete type 'decltype(pipeable_access::impl<view<reverse_fn> >::pipe(static_cast<ranges::v3::split_view<ranges::v3::iterator_range<std::_String_iterator<std::_String_val<std::_Simple_types<char> > >, std::_String_iterator<std::_String_val<std::_Simple_types<char> > > >, ranges::v3::view::split_fn::element_pred<std::basic_string<char, std::char_traits<char>, std::allocator<char> > &> > &&>(arg), pipe))' (aka 'void') to 'std::_Node_flags' for 1st argument
range\v3\utility\functional.hpp:725: candidate template ignored: substitution failure [with Arg = void, Pipe = ranges::v3::view::view<ranges::v3::detail::pipeable_binder<std::_Binder<std::_Unforced, ranges::v3::view::join_fn &, const std::_Ph<1> &, char> > >, _concept_requires_724 = false, $3 = nullptr]: cannot form a reference to 'void'
range\v3\utility\functional.hpp:734: candidate template ignored: requirement 'false || (is_pipeable<void>() && is_pipeable<ranges::v3::view::view<ranges::v3::detail::pipeable_binder<std::_Binder<std::_Unforced, ranges::v3::view::join_fn &, const std::_Ph<1> &, char> > > >())' was not satisfied [with Pipe0 = void, Pipe1 = ranges::v3::view::view<ranges::v3::detail::pipeable_binder<std::_Binder<std::_Unforced, ranges::v3::view::join_fn &, const std::_Ph<1> &, char> > >, _concept_requires_733 = false]

Нашел способ сделать это:

    auto temp_container = str
              | view::split('.')
              | ::ranges::to_vector
              | action::reverse
              ;
    std::string output = temp_container 
              | view::all
              | view::join('.')
              ;

Есть идеи получше?


person joyqat    schedule 18.07.2019    source источник
comment
Почти уверен, что они что-то сломали, так как теперь простой ranges::view::group_by, похоже, вообще не работает. Я считаю, что проблема заключается в том, что они могли нарушать диапазоны диапазонов, но я не могу это подтвердить.   -  person Fureeish    schedule 18.07.2019
comment
@Fureeish Ни одна из версий ranges-v3, доступных на godbolt (0.3.0, 0.3.5, 0.3.6, trunk), не компилирует этот фрагмент кода (либо на gcc, либо на clang). Пример здесь. Но я также не знаю, о каких временных рамках вы думаете, когда говорите «сейчас».   -  person Max Langhof    schedule 18.07.2019
comment
@MaxLanghof говорит о выпуске 0.5.0. Godbolt также не компилирует простые group_bys. Учитывая тот факт, что диапазоны все еще находятся в разработке, я бы не хотел пытаться угадать, что не так на данный момент. Код должен работать.   -  person Fureeish    schedule 18.07.2019
comment
view::group_by тесты Range-v3 проходят. Что у тебя не работает @Fureeish?   -  person Eric Niebler    schedule 25.07.2019
comment
Вы не можете направить временный контейнер в адаптер представления, потому что это создаст висячие ссылки. Сначала сохраните вектор в именованной переменной.   -  person Eric Niebler    schedule 26.07.2019


Ответы (1)


view::split возвращает либо входной диапазон, либо прямой диапазон в зависимости от адаптированного диапазона. view::reverse требуется по крайней мере двунаправленный диапазон, чтобы выполнить итерацию в обратном направлении.

В принципе можно реализовать split_view, который является двунаправленным диапазоном. Проблема в том, что конструктору уже потребуется полный обход адаптированного диапазона, чтобы найти последний элемент, что делает его операцией o(n). И насколько я знаю, построение представлений должно быть o(1).

person sv90    schedule 18.07.2019
comment
Спасибо за ваше объяснение. Я думаю, что может быть что-то вроде as_list или as_vector, поэтому мы можем передавать операции, такие как split | as_vector | reverse | join, когда держим конструкцию представления o(1). - person joyqat; 19.07.2019
comment
@joyqat: у вас не может быть конструкции O (1), потому что это вектор. Построение его должно выполнить O(n) копий. Таким образом, вы можете просто вставить результат split в вектор, а затем применить reverse и join. Почему все должно быть в одной строке? - person Nicol Bolas; 19.07.2019
comment
@NicolBolas Потому что, я думаю, такие операции с трубами могут устранить временные и быть надежными. Более того, это действительно здорово, если я могу сделать это с помощью C++. :П - person joyqat; 19.07.2019
comment
@joyqat есть ranges::to_vector и range::to<Container> для создания std::vector или произвольного Container из диапазона. Но вы не можете передать это так, поскольку to_vector возвращает временный вектор, который является значением r, которое не может передаваться с представлениями. Таким образом, вам придется сохранить вектор как промежуточный результат. Это хорошо, поскольку в противном случае возникнут оборванные ссылки. - person sv90; 19.07.2019
comment
@joyqat: Как бы вы устранили временное? Чтобы иметь возможность реверсировать его, требуется, чтобы он сначала был построен, и поэтому должен выделять память и копировать-инициализировать каждый элемент. Полученный контейнер затем должен использоваться каждой последующей операцией в цепочке. - person Nicol Bolas; 19.07.2019