The C++ std algorithms define a number of algorithms that take an input and an output sequence, and create the elements of the output sequence from the elements of the input sequence. (Best example being std::transform
.)
The std algorithms obviously take iterators
, so there's no question that the container for the OutputIterator
has to exist prior to the algorithm being invoked.
That is:
std::vector<int> v1; // e.g. v1 := {1, 2, 3, 4, 5};
std::vector<int> squared;
squared.reserve(v1.size()); // not strictly necessary
std::transform(v1.begin(), v1.end(), std::back_inserter(squared),
[](int x) { return x*x; } ); // λ for convenience, needn't be C++11
And this is fine as far as the std library goes. When I find iterators too cumbersome, I often look to Boost.Range to simplify things.
In this case however, it seems that the mutating algorithms in Boost.Range also use OutputIterators
.
So I'm currently wondering whether there's any convenient library out there, that allows me to write:
std::vector<int> const squared = convenient::transform(v1, [](int x) { return x*x; });
-- and if there is none, whether there is a reason that there is none?
Edit: example implementation (not sure if this would work in all cases, and whether this is the most ideal one):
template<typename C, typename F>
C transform(C const& input, F fun) {
C result;
std::transform(input.begin(), input.end(), std::back_inserter(result), fun);
return result;
}
(Note: I think convenient::transform
will have the same performance characteristics than the handwritten one, as the returned vector won't be copied due to (N)RVO. Anyway, I think performance is secondary for this question.)
Edit/Note: Of the answers(comments, really) given so far, David gives a very nice basic generic example.
And Luc mentions a possible problem with std::back_inserter
wrt. genericity.
Both just go to show why I'm hesitating to whip this up myself and why a "proper" (properly tested) library would be preferable to coding this myself.
My question phrased in bold above, namely is there one, or is there a reason there is none remains largely unanswered.
The Boost.Range.Adaptors can be kind of seen as container-returning algorithms. Why not use them?
The only thing that needs to be done is to define a new range adaptor create<T>
that can be piped into the adapted ranges and produces the desired result container:
template<class T> struct converted{}; // dummy tag class
template<class FwdRange, class T>
T operator|(FwdRange const& r, converted<T>){
return T(r.begin(), r.end());
}
Yep, that's it. No need for anything else. Just pipe that at the end of your adaptor list.
Here could be a live example on Ideone. Alas, it isn't, because Ideone doesn't provide Boost in C++0x mode.. meh. In any case, here's main
and the output:
int main(){
using namespace boost::adaptors;
auto range = boost::irange(1, 10);
std::vector<int> v1(range.begin(), range.end());
auto squared = v1 | transformed([](int i){ return i * i; });
boost::for_each(squared, [](int i){ std::cout << i << " "; });
std::cout << "\n========================\n";
auto modded = squared | reversed
| filtered([](int i){ return (i % 2) == 0; })
| converted<std::vector<int>>(); // gimme back my vec!
modded.push_back(1);
boost::for_each(modded, [](int i){ std::cout << i << " "; });
}
Output:
1 4 9 16 25 36 49 64 81
========================
64 36 16 4 1