Как объединить back_inserter с преобразованием C ++ - PullRequest
0 голосов
/ 29 апреля 2018

Как я могу обернуть OutputIterator, такой как back_inserter_iterator, с преобразованием? Рассмотрим

std::vector<double> xx;
std::vector<double> yy;
std::vector<double> diff;
auto ba = std::back_inserter(diff);
std::set_difference(xx.begin(), xx.end(), yy.begin(), yy.end(), ba);

Я бы хотел применить свободную функцию f(double) или g(std::vector<double>::iterator), прежде чем вернуться к вектору различий:

В частности, как я могу хранить адреса элементов diff (или итераторов) вместо самих элементов.

std::vector<double&> diff;
auto baAdr = ??? std::back_inserter( ??? (diff)); 
std::set_difference(xx.begin(), xx.end(), yy.begin(), yy.end(), baAdr);

Из соображений производительности (реальные данные велики) я не хочу создавать временный вектор и std::transform из него. Это также не работает для не копируемых, подвижных типов.

Я могу использовать повышение.

Ответы [ 2 ]

0 голосов
/ 29 апреля 2018

С boost::function_output_iterator:

#include <vector>
#include <algorithm>
#include <boost/function_output_iterator.hpp>

int main() 
{
    std::vector<double> xx;
    std::vector<double> yy;
    std::vector<const double*> diff;  // const pointers, or else you
                                      // need a const_cast in lambda

    std::set_difference(xx.begin(), xx.end(), yy.begin(), yy.end(),
        boost::make_function_output_iterator(
            [&diff](const double& d) { diff.push_back(&d); }
        )
    );
}
0 голосов
/ 29 апреля 2018

Вероятно, есть что-то встроенное для повышения, но вот моя хакерская попытка написать собственный итератор:

template <typename T, typename FN>
struct transform_iterator {
    transform_iterator(T &t, FN fn)
      : _t{t}
      , _fn{std::move(fn)} { }

    transform_iterator<T, FN>& operator * () { return *this; }

    transform_iterator<T, FN>& operator ++ () { return *this; }

    template <typename V>
    transform_iterator<T, FN>& operator = (V const &v) {
        _t.push_back(_fn(v));
        return *this;
    }

    T &_t;
    FN _fn;
};

Это возьмет функцию и выполнит ее всякий раз, когда что-то пытается назначить итератору (я думаю , как обычно работают такие вещи, как back_inserter). Тривиальная вспомогательная функция может создавать итераторы:

template <typename T, typename FN>
auto make_transform_iterator(T &t, FN fn) {
    return transform_iterator<T, FN>{t, std::move(fn)};
};

Наконец, iterator_traits должен быть специализированным, поэтому transform_iterator будет работать с алгоритмами.

namespace std {
    template <typename T, typename FN>
    struct iterator_traits<transform_iterator<T, FN>> {
        using value_type = typename T::value_type;
    };
}

В iterator_traits должны быть установлены другие типы, но этого было достаточно для моего тестирования; Ваш пробег будет варьироваться.

Мой main выглядит так:

int main() {
    std::vector<int> xx{1, 2, 3};
    std::vector<int> yy{1, 3, 5};
    std::vector<int> diff;

    auto ba = make_transform_iterator(diff, [](auto v) { return v + 10; });
    std::set_difference(std::begin(xx), std::end(xx),
                        std::begin(yy), std::end(yy),
                        ba);
    for(auto const &v: diff) {
        std::cout << v << '\n';
    }
    return 0;
}

Вы можете расширить это для работы с общими итераторами вывода вместо просто типов, которые поддерживают push_back.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...