Reputation: 11503
By using boost perhaps or another way?
I want to create a function, to grab a subset of deque d
from (and including) index iStart to index 0, into a new deque, but also to set that those values in d
to 0. I thought about this:
std::deque<int> CreateSubset(std::deque<int>& d, int iStart )
{
int iSubsetSize = iStart+1;
std::deque<int> subset(iSubsetSize); // initialise a deque of a certain size.
std::deque<int>::iterator it = d.begin();
subset.assign (it, d.begin()+iStart+1);
for(;it != d.begin()+iStart+2; it++)
{
*it = 0;
}
return subset;
}
But it looks horrible to me - is there a nicer way?
Upvotes: 5
Views: 2168
Reputation: 24956
Since the Container concept requires container types to define iterator
and const_reference
it is even possible to have jogojapan's answer a little more generic without having more than one template parameter.
template <typename Container>
Container extract(typename Container::iterator from,
typename Container::iterator to,
typename Container::const_reference replacement)
{
using std::make_move_iterator, std::fill;
Container new_container(make_move_iterator(from),
make_move_iterator(to));
fill(from,to,replacement);
return new_container;
}
Where you can go for
deque<int> d { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 };
auto d2 = extract<deque<int>>(begin(d),begin(d) + 3, 0);
The downside is that template argument deduction won't work that nice anymore.
The benefit is that you can use the code for almost all STL containers like vectors, deque, list, forward_list, set, map ... since they all have at least a forward_iterator as their iterator type. You can also specify a "default" replacement.
It would also be possible to use a pointer instead of a reference so you can decide whether you want to erase (passing nullptr) or replace the elements with a certain value (passing a valid pointer to a respective object).
Upvotes: 3
Reputation: 69997
Here is how I'd do it in C++11. I believe the code is fairly elegant, and I don't think there is anything particularly inefficient about it:
#include <iostream>
#include <deque>
#include <iterator>
#include <algorithm>
template <typename ForwardIt>
std::deque<int> extract(ForwardIt from, ForwardIt to)
{
using std::make_move_iterator;
std::deque<int> d2(make_move_iterator(from),
make_move_iterator(to));
std::fill(from,to,0);
return d2;
}
The extract()
function template takes two forward iterators, moves the content between them to a newly created deque and sets it to 0 in the original.
The template as written makes two assumptions:
Both assumptions can be relaxed by introducing further template parameters or function arguments.
As you can see, I use std::make_move_iterator
to convert the input iterators to move iterators, thus causing the elements to be moved (rather than copied) to the destination. As long as these are int
, it won't really make a difference, though.
I use the std::fill
algorithm to set the original elements to 0.
This is how you could call this function template:
int main()
{
/* Creating d. */
std::deque<int> d
{ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 };
/* Extracting the first three elements into a new deque. */
auto d2 = extract(begin(d),begin(d) + 3);
/* Printing the results. */
std::cout << "d:\n";
for (const auto &elem : d)
std::cout << elem << ',';
std::cout << "\n\nd2:\n";
for (const auto &elem : d2)
std::cout << elem << ',';
std::cout << std::endl;
return 0;
}
Upvotes: 9