Как реализовать адаптер диапазона с boost::joined_range

Вот пример адаптера диапазона, основанного на Реализовать адаптер диапазона с аргументами:

#include <boost/range/join.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/iterator/permutation_iterator.hpp>
#include <vector>
#include <list>
#include <iterator>
#include <iostream>

template <typename Range, typename Index>
class permutation_range :
        public boost::iterator_range<
                boost::permutation_iterator<
                        typename boost::range_iterator<Range>::type,
                        typename boost::range_iterator<Index>::type>>
{
    using value_type = typename boost::range_value<Range>::type;
    using replaced_iterator = boost::permutation_iterator<
                            typename boost::range_iterator<Range>::type,
                            typename boost::range_iterator<Index>::type>;
    using base_t = boost::iterator_range<replaced_iterator>;
public:
    permutation_range(Range& r, Index& i)
            : base_t(replaced_iterator(boost::begin(r), boost::begin(i)),
                    replaced_iterator(boost::end(r), boost::end(i)))
    {}
};

template <typename Range, typename Index>
permutation_range<Range, Index>
permutation(Range& r, Index& i)
{
    return permutation_range<Range, Index>(r, i);
}

int main()
{
    std::vector<int> v1{99, 1, 99,  2, 99, 3};
    std::list<int> indexer{1, 3, 5};
    boost::copy(permutation(v1, indexer),
                std::ostream_iterator<int>(std::cout, " "));
}

Вывод

1 2 3

Я хочу адаптировать приведенное выше для использования boost::joined_range. Другими словами, возьмите два вектора, а затем соедините их в один более длинный диапазон внутри permutation_range. Идея проста:

Пример, который выводит 2 4 6:

int main()
{
    std::vector<int> v1{1, 2, 3};
    std::vector<int> v2{4, 5, 6};
    std::list<int> indexer{1, 3, 5};
    boost::copy(permutation(v1, v2, indexer),
                std::ostream_iterator<int>(std::cout, " "));
}

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

ПОСЛЕДНЯЯ РЕДАКТИРОВАНИЕ (Решение с небольшим хаком, позволяющим инициализировать диапазон перед базовым классом)

#include <boost/range/join.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/iterator/permutation_iterator.hpp>
#include <vector>
#include <list>
#include <iterator>
#include <iostream>

template <typename R1, typename R2>
struct initialize_me_first
{
    initialize_me_first(typename boost::range::joined_range<const R1, const R2> j)
    : j_range(j)
    {}
    typename boost::range::joined_range<const R1, const R2> j_range;
};

template <typename Range1, typename Range2, typename Index>
class permutation_range :
        public initialize_me_first<Range1, Range2>,
        public boost::iterator_range<
                boost::permutation_iterator<
                        typename boost::range_iterator<
                            boost::range::joined_range<Range1, Range2>>::type,
                        typename boost::range_iterator<Index>::type>>
{
    using value_type = typename boost::range_value<Range1>::type;
    using replaced_iterator = boost::permutation_iterator<
                    typename boost::range_iterator<
                            boost::range::joined_range<Range1, Range2>>::type,
                            typename boost::range_iterator<Index>::type>;
    using base_t = boost::iterator_range<replaced_iterator>;
    using init = initialize_me_first<Range1, Range2>;
public:
    permutation_range(const Range1& r1, const Range2& r2, const Index& i)
            : init(boost::join(r1, r2)),
              base_t(replaced_iterator(boost::begin(init::j_range),
                                       boost::begin(i)),
                    replaced_iterator(boost::end(init::j_range),
                                       boost::end(i)))
    {}
};

template <typename Range1, typename Range2, typename Index>
permutation_range<const Range1, const Range2, const Index>
permutation(const Range1& r1, const Range2& r2, const Index& i)
{
    return permutation_range<const Range1,
                             const Range2,
                             const Index>(r1, r2, i);
}

int main()
{
    std::vector<int> v1{1, 2, 3};
    std::vector<int> v2{4, 5, 6};
    std::list<int> indexer{1, 3, 5};
    boost::copy(permutation(v1, v2, indexer),
                std::ostream_iterator<int>(std::cout, " "));
}

person Jesse Good    schedule 24.07.2012    source источник


Ответы (1)


Чтобы объединить два диапазона, используйте boost::join:

#include <boost/range/join.hpp>

boost::copy(permutation(boost::join(v1, v2), indexer), ...

Но измените свои параметры Range, чтобы они передавались по константной ссылке вместо неконстантной ссылки. Нет причин требовать модифицируемых версий этих аргументов, поскольку вы фактически не изменяете их содержимое.

person Rob Kennedy    schedule 24.07.2012
comment
Спасибо, я также понял, что могу вставить boost::join внутри permutation, если хочу сохранить синтаксис permutation(v1, v2, indexer). Однако моей первоначальной целью было изменить permutation_range на использование boost::join (хотя не знаю, возможно ли это). - person Jesse Good; 25.07.2012
comment
Это будет сложнее. Ваш permutation_range должен иметь копию объединенного диапазона, который он создает — он должен передать одно и то же значение r для обоих объектов replaced_iterator, которые он создает. Этот диапазон должен существовать до инициализации базового класса. Это решаемо, но у вас также есть добавленное синтаксическое усложнение фактически необязательного параметра (v2), появляющегося в середине списка аргументов. Если вызывающему абоненту нужны объединенные диапазоны, позвольте ему нести ответственность за это, а ваш интерфейс должен быть простым и сосредоточенным только на одной вещи. - person Rob Kennedy; 25.07.2012
comment
Хороший вопрос об интерфейсе и большое спасибо за помощь. После ваших комментариев я смог кое-что придумать (см. EDIT в вопросе), однако, как я могу заставить диапазон существовать до инициализации базового класса? - person Jesse Good; 25.07.2012
comment
Я придумал решение, унаследовав два класса (см. ПОСЛЕДНИЕ РЕДАКТИРОВАНИЯ). Спасибо еще раз! - person Jesse Good; 25.07.2012