简体   繁体   中英

is there an elegant way to transfer a subset of one deque to another?

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?

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:

  1. Although the source iterators may refer to anything, the destination is always a deque;
  2. The default value you want to reset the original elements to is 0.

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;
}

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).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM