I would like to copy up to N elements.
template< class InputIt, class Size, class OutputIt>
OutputIt myCopy_n(InputIt first, InputIt last, Size count, OutputIt result)
{
Size c = count;
while (first != last && c > 0) {
*result++ = *first++;
--c;
}
return result;
}
is there a way to do this with std functions? I could also:
template< class InputIt, class Size, class OutputIt>
OutputIt myCopy_n(InputIt first, InputIt last, Size count, OutputIt result)
{
if(std::distance(first, last) > count)
return std::copy_n(first,count,result);
return std::copy(first,last,result);
}
however, besides being cumbersome, it goes over the range twice (distance, copy). If i'm using a transform iterator, or filter iterator, those are O(N) unnecessary calls to my filter/transform function.
template <class InputIt, class OutputIt>
OutputIt copy_n_max(InputIt begin, InputIt end, OutputIt last, size_t count)
{
return std::copy_if(begin, end, last,
[&count](typename std::iterator_traits<InputIt>::reference)
{ return count--> 0; });
}
int main()
{
std::vector<int> v({1,2,3,4,5,6,7,8,9}), out;
copy_n_max(v.begin(), v.end(), std::back_inserter(out), 40);
for(int i : out) std::cout <<i << " ,";
}
outputs 1,2,3,4,5,6,7,8,9,
however, this will continue until end, and not count times. so still, more unnecessary calls to my filter/transform function...
If you have access to the whole data structure, and therefore its size, you can use the following:
std::vector<int> v1, v2;
std::copy_n(v2.begin(), std::min(NUM, v2.size()), std::back_inserter(v1));
If you have access to only iterators, I don't know how to do this using only std functions without calculating the distance. This is cheap for random-access iterators but duplicates work for other types.
std::vector<int>::iterator i_begin, i_end, o_begin;
std::copy_n(i_begin, std::min(NUM, std::distance(i_begin, i_end)), o_begin);
I would go for something like this:
template <class InputIt, class OutputIt>
OutputIt copy_n_max(InputIt begin, InputIt end, OutputIt last, size_t count)
{
return std::copy_if(begin,
end,
last,
[&count](typename std::iterator_traits<InputIt>::reference) -> bool
{
if (count > 0)
{
--count;
return true;
}
return false;
});
}
Using copy_if
predicate to check whether or not enough of this input was copied.
Main advantage I see here is no extra std::distance
involved.
There's a simple way to use the std::copy_if
-overload added by C++11 for your task (Only needs InputIterators):
template< class InputIt, class Size, class OutputIt>
OutputIt myCopy_n(InputIt first, InputIt last, Size count, OutputIt result)
{
return std::copy_if(first, last, result,
[&](typename std::iterator_traits<InputIt>::reference)
{return count && count--;});
}
BTW: It gets even better in C++14, no need for a variable or such a complicated argument:
std::copy_if(first, last, result,
[count = some_complicated_expression](auto&&) mutable
{return count && count--;});
You can use copy_if
with a custom predicator, and it works for older versions of c++.
#include <algorithm>
#include <iostream>
#include <vector>
#include <iterator>
struct LimitTo
{
LimitTo( const int n_ ) : n(n_)
{}
template< typename T >
bool operator()( const T& )
{
return n-->0;
}
int n;
};
int main()
{
std::vector< int > v1{ 1,2,3,4,5,6,7,8 };
std::vector< int > v2;
std::copy_if( std::begin(v1), std::end(v1), std::back_inserter(v2), LimitTo(3) );
std::copy( std::begin(v1), std::end(v1), std::ostream_iterator<int>(std::cout,", ") );
std::cout << std::endl;
std::copy( std::begin(v2), std::end(v2), std::ostream_iterator<int>(std::cout,", ") );
std::cout << std::endl;
}
This example copies n elements, using predicator LimitTo
.
来源:https://stackoverflow.com/questions/26119212/stdcopy-n-elements-or-to-the-end