问题
consider this code snippet : iteration over one container of a first type T1 for creating a second container of a second type T2 applying a transformation function T1->T2 but only for T1 elements verifying a predicate (T1 -> bool )
(is Odd in the following example).
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));
});
What I don't like in this code is the capture on the lambda. Is there a way to combine std::copy_if and std::transform to achieve the same result in a more elegant and concise way ?
回答1:
Here is a transform_if
template that takes the usual input iterator pair, an output iterator and a predicate as well as a transformation function object.
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;
}
}
You can use it for your example like the following.
transform_if(myIntVector.cbegin(), myIntVector.cend(),
std::back_inserter(myStringVectorOfOdd),
[](int n){ return n % 2 != 0; },
[](int n){ return std::to_string(n); });
It's not super concise, but filtering and transformation are well separated into to capture-free lambdas, and the algorithm itself idiomatically works on iterators.
As range libraries offer better support for composing algorithms, here is the same based on 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); });
回答2:
With range-v3, it would be:
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
来源:https://stackoverflow.com/questions/52400746/how-to-combine-stdcopy-if-and-stdtransform