【问题标题】:How to combine back_inserter with a transformation, C++如何将 back_inserter 与转换相结合,C++
【发布时间】:2018-10-09 22:37:32
【问题描述】:

如何使用转换包装像 back_inserter_iterator 这样的 OutputIterator? 考虑

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&lt;double&gt;::iterator)

具体来说,我如何存储差异元素(或迭代器)的地址而不是元素本身。

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

出于性能原因(实际数据很大),我不想从中构造一个临时向量和std::transform。它也不适用于不可复制的可移动类型。

我可以使用 boost。

【问题讨论】:

  • boost::function_output_itetator 也许?
  • @JohanLundberg 你看过example吗?就像,你真的想要make_function_output_iterator([&amp;](double d){ diff.push_back(f(d)); })
  • 我认为 set_difference 不会将元素的地址提供给输出迭代器。也许如果您在*iteratoroperator=() 中进行引用,但我不确定这是否得到保证,即您可能会获得临时地址。
  • @PaulR 我不认为复制实际值是允许的实现。而且,只能复制类型的子集。
  • 我刚刚看了看,在 libstdc++ 7.2 中没有临时的。您是对的,额外的复制通常效率低下,因此不能在一般实现中使用。至于不可复制的类型,好吧,标准说:“复制元素......”,尽管使用正确的 operator= 你很高兴:-)

标签: c++ boost iterator c++14


【解决方案1】:

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); }
        )
    );
}

【讨论】:

  • 谢谢!这正是我所希望的,我在浏览 boost 文档时没有找到 make_function_output_iterator。但是,我在用 Visual Studio 编译它时遇到了麻烦。 godbolt.org/g/Qhtz3A
  • @JohanLundberg 可能与this 有关,也许?尝试将 lambda 保存到 std::function 并查看它是否可以编译。
  • 是的,有效!谢谢你。根据设计的答案。
  • 不过in all compilers 没问题。
【解决方案2】:

可能有一些内置的东西可以提升,但这是我编写自己的迭代器的骇人听闻的尝试:

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 的类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-09
    • 1970-01-01
    • 2013-04-19
    • 2017-10-21
    • 2010-12-21
    • 2016-05-22
    • 2023-01-02
    相关资源
    最近更新 更多