【问题标题】:Move all elements which satisfy some condition from one container to another, i.e. I'm looking for some kind of "move_if"将满足某些条件的所有元素从一个容器移动到另一个容器,即我正在寻找某种“move_if”
【发布时间】:2015-08-22 11:55:06
【问题描述】:

给定

std::vector<T> first = /* some given data */, second;

我想移动所有满足某些条件cond(e) 的元素firstsecond,即类似

move_if(std::make_move_iterator(first.begin()),
    std::make_move_iterator(first.end()),
    std::back_inserter(second), [&](T const& e)
{
        return cond(e);
});

我无法使用 算法库 确定这一点。那么,我该怎么做呢?

【问题讨论】:

  • std::copy_if 后跟 std::remove_if 怎么样?
  • @JoachimPileborg 为什么你认为remove_copy_if 能胜任这项工作?
  • copy_ifmove_iterator。确保您的谓词通过 const ref 获取其参数。
  • @T.C.对,这会将元素移动到second。但是,first 仍然包含这些元素(即使已经移动了数据)。那么,如何从first 中删除它们?

标签: c++ c++11 c++14


【解决方案1】:

如果移出的元素可以保留在first 中的位置,那么只需将copy_ifmove_iterator 一起使用。

std::copy_if(std::make_move_iterator(first.begin()),
             std::make_move_iterator(first.end()),
             std::back_inserter(second), cond);

如果应该从first 中删除被移动的元素,我会这样做

// partition: all elements that should not be moved come before 
// (note that the lambda negates cond) all elements that should be moved.
// stable_partition maintains relative order in each group
auto p = std::stable_partition(first.begin(), first.end(),
                               [&](const auto& x) { return !cond(x); });
// range insert with move
second.insert(second.end(), std::make_move_iterator(p),
                            std::make_move_iterator(first.end()));
// erase the moved-from elements.
first.erase(p, first.end());

partition_copy 加上move_iterator,然后是赋值:

std::vector<T> new_first;
std::partition_copy(std::make_move_iterator(first.begin()),
                    std::make_move_iterator(first.end()),
                    std::back_inserter(second), std::back_inserter(new_first), cond);
first = std::move(new_first);

【讨论】:

  • 您不必在中间情况下反转cond,您可以使用范围 [first.begin(), p) 代替 [p, first.end()),虽然我想它可以节省压缩结果
【解决方案2】:

move_if 不存在的原因是它会使库膨胀。将copy_if 与移动迭代器一起使用或自己编写。

copy_if(move_iterator<I>(f), move_iterator<I>(l), out);

这是Jonas_No 在channel9 的实现。

template <typename FwdIt, typename Container, typename Predicate>
inline FwdIt move_if(FwdIt first, FwdIt last, Container &cont, Predicate pred)
{
    if (first == last)
        return last; // Empty so nothing to move
    const size_t size = count_if(first, last, pred);
    if (size == 0)
        return last; // Nothing to move
    cont.resize(size);
    FwdIt new_end = first;
    auto c = cont.begin();
    for (auto i = first; i != last; ++i)
    {
        if (pred(*i)) // Should it move it ?
            *c++ = move(*i);
        else
            *new_end++ = move(*i);
    }
    return new_end;
}

【讨论】:

    【解决方案3】:

    @T.C.提供了一个完美的工作解决方案。但是,乍一看,人们可能不明白该代码的意图是什么。所以,它可能并不完美,但我更喜欢这样的东西:

    template<class InputIt, class OutputIt, class InputContainer, class UnaryPredicate>
    OutputIt move_and_erase_if(InputIt first, InputIt last, InputContainer& c, OutputIt d_first, UnaryPredicate pred)
    {
        auto dist = std::distance(first, last);
        while (first != last)
        {
            if (pred(*first))
            {
                *d_first++ = std::move(*first);
                first = c.erase(first);
                last = std::next(first, --dist);
            }
            else
            {
                ++first;
                --dist;
            }
        }
        return d_first;
    }
    

    【讨论】:

    • 迭代调用 c.erase(first); 对于将元素存储在连续存储中的容器来说,这听起来不是最好的主意
    • 乍一看 std::next(first, --dist); 看起来像一个错误,因为您将 last 设置为位置 current+(previous container size)-1
    • 逐个删除元素不是很高效。每次间隙都将通过移动元素来关闭。因此,接受的答案是要走的路。
    猜你喜欢
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-29
    • 2020-11-25
    • 1970-01-01
    • 2022-12-04
    • 2020-05-02
    相关资源
    最近更新 更多