如何结合使用std::copy_if和std::transform?

13

考虑以下代码片段:迭代第一个类型T1的容器,创建第二个类型T2的容器,应用转换函数T1-> T2,但仅针对验证谓词(T1-> bool)的T1元素。

(在下面的示例中为“是奇数”)。

std::vector<int> myIntVector;
myIntVector.push_back(10);
myIntVector.push_back(15);
myIntVector.push_back(30);
myIntVector.push_back(13);

std::vector<std::string> myStringVectorOfOdd;

std::for_each(myIntVector.begin(), myIntVector.end(),
    [&myStringVectorOfOdd](int val)
{
    if (val % 2 != 0)
        myStringVectorOfOdd.push_back(std::to_string(val));

});

我不喜欢这段代码中lambda的捕获方式。有没有一种更优雅、更简洁的方法来结合std::copy_if和std::transform以达到相同的结果?


2
为什么你不喜欢 capture?你将其作为引用捕获并将元素推送到其中。我不确定其中有什么不足之处。虽然它不像 Python 中优美的 myStringVectorOfOdd = [str(x) for x in myIntVector) if x % 2 == 1],但也不算糟糕。 - paxdiablo
2个回答

14

这里是一个transform_if模板,它接受通常的输入迭代器对、输出迭代器和谓词,以及一个转换函数对象。

template <class InputIt, class OutputIt, class Pred, class Fct>
void transform_if(InputIt first, InputIt last, OutputIt dest, Pred pred, Fct transform)
{
   while (first != last) {
      if (pred(*first))
         *dest++ = transform(*first);

      ++first;
   }
}

你可以像以下示例一样使用它。

transform_if(myIntVector.cbegin(), myIntVector.cend(),
    std::back_inserter(myStringVectorOfOdd),
    [](int n){ return n % 2 != 0; },
    [](int n){ return std::to_string(n); });

虽然不是非常简洁,但过滤和转换被很好地分离成不含捕获的lambda表达式,算法本身习惯地在迭代器上运行。

由于范围库提供更好的算法组合支持,因此这里基于Boost range实现了相同的功能:

#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>

using boost::adaptors::filtered;

boost::transform(myIntVector | filtered([](int n){ return n % 2 != 0; }),
    std::back_inserter(myStringVectorOfOdd), [](int n){ return std::to_string(n); });

9
使用 range-v3,它将是这样的:
const std::vector<int> myIntVector {10, 15, 30, 13};

std::vector<std::string> myStringVectorOfOdd = myIntVector
    | ranges::view::filter([](int i){ return i % 2 != 0; })
    | ranges::view::transform([](int i){ return std::to_string(i); });

Demo


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接