【问题标题】:using std::transform the final vector remains empty使用 std::transform 最终向量保持为空
【发布时间】:2020-03-22 22:04:46
【问题描述】:

我不经常使用 std::transform,但我发现它非常有用,我开始用这个算法替换一些 for 循环。

这里有什么问题?我想保留向量 vec 的所有代码 > 100 的元素。我希望有一个新的 std::vector 有 3 个元素:133、144 和 155。但是在算法之后大小为 0。出了什么问题?

 TEST_CASE("testing trasf1", "[tras1]") {

    std::vector<Test2> vec {
            {1,1},
            {3,3},
            {11,11},
            {12,12},
            {133,133},
            {19,19},
            {21,21},
            {22,22},
            {23,23},
            {144,144},
            {155,155}

    };

    std::vector<uint32_t> final_v {};
    final_v.reserve(vec.size());

    transform(begin(vec), end(vec), begin(final_v), [] (const Test2& elem) {
        if ( elem.getCode() > 100)
            return elem.getCode();
    });

    //REQUIRE(final.size() == 3);

    cout << final_v.size() << endl;

    for (const auto i : final_v) {
        cout << i << endl;
    }

}

【问题讨论】:

  • 不要使用final,它是关键字。其次,在final 上调用resize 而不是reserve。或者使用 back_inserter。您将值放入空的最终值。
  • 你的 lambda 似乎没有声明它的返回类型。我觉得应该是[] (const Test2&amp; elem) -&gt; int {...};
  • @rafix07 处理了您的评论
  • 您的 lambda 仅在 elem.getCode() &gt; 100 时返回定义的值。请记住,lambda 的工作方式与任何其他函数相同,所有代码路径都必须返回一个值。 std::transform 将始终输出与输入中一样多的元素,因此 final.size() == 3 没有办法。我不认为(有待更正)有一个std 算法可以一步完成过滤和转换,因此您要么必须使用循环,要么分两步完成.
  • @Stu 我也不这么认为。使用 C++20 中的范围和视图,这将非常容易。对于转换,可以将back_inserter 与某种optional_inserter 相匹配,得到std::optional&lt;T&gt; 并在它不是nullopt 时插入。

标签: c++ stl c++14


【解决方案1】:

transform 不会将元素插入到输出序列中,它只是写入*iter 并递增迭代器。

如果要插入到序列中,请使用std::back_inserter(final) 作为输出迭代器。

或者,首先调用final.resize(vec.size()),将输出向量设置为正确的大小。请注意,这会将向量元素初始化为零,因此对于大向量会产生明显的时间开销。

【讨论】:

  • 如果我调用调整大小而不是保留最终向量有 22 个元素(2*初始大小)
  • resize 也意味着在上面的示例中向量包含大量垃圾数据,如果 OP 只想要包含这三个元素的向量,则必须将其过滤掉。 reserve + back_inserter 如果我不错过任何东西,似乎显然更胜一筹。编辑:没关系,transform 总是输出相同数量的元素。
  • 如果您调用resize 使用back_inserter,那么您将获得两倍的元素。做一个或另一个。
  • @Captain Nemo 是 resizebegin(final_v)reserveback_inserter 不是 resizeback_inserter
【解决方案2】:

在一次操作中进行过滤和转换:


template<class Container>
struct optional_inserter_iterator: std::back_insert_iterator //convenience
{
    using std::back_insert_iterator::*;
    optional_inserter_iterator(Container &c) : std::back_insert_iterator(c)
    {}

    optional_inserter_iterator & operator=(std::optional<Container::value_type> &&opt)
    {
        if(opt)
            std::back_insert_iterator::operator=(*std::move(opt))
        return *this;
    }
}

在转换中使用它并让你的 lambda 返回一个可选的

(未经测试)

编辑:有一些理由不继承 std::back_insert_iterator。如果有人对真正正确的版本感兴趣,我可以做一个。

【讨论】:

    猜你喜欢
    • 2013-02-07
    • 1970-01-01
    • 2016-03-01
    • 1970-01-01
    • 2014-02-27
    • 2022-06-22
    • 2011-08-08
    • 2021-09-22
    相关资源
    最近更新 更多