2010-11-09 4 views
0

J'ai un conteneur std :: vector et je voudrais le diviser efficacement en sous-gammes avec x éléments dans chacun. Le conteneur d'origine n'est pas nécessaire, les éléments doivent donc être déplacés et non copiés dans les sous-gammes.Diviser la gamme en sous-gammes

J'ai réussi à faire le fractionnement en utilisant la copie, mais je ne sais pas comment le faire avec les affectations de déplacement?

range.insert(range.end(), new_items.begin(), new_items.end()); 
    while(range.size() >= x) 
    { 
     sub_ranges.push_back(std::vector<int>(range.begin(), range.begin() + x)); 
     range = std::vector<int>(range.begin() + x, range.end()); 
    } 

EDIT:

Des progrès ... pas encore tout à fait là, et un peu laid

while(range.size() >= x) 
    { 
     std::vector<short> sub_range(x); // Unnecessary allocation? 
     std::move(range.begin(), range.begin() + x, sub_range.begin()); 
     sub_ranges_.push_back(std::move(sub_range)); 

     std::move(range.begin() + x, range.end(), range.begin()); 
     range.resize(range.size() - x); 
    } 

Répondre

1

Vous pouvez utiliser std::make_move_iterator dans <iterator> pour envelopper votre iterator dans un move_iterator. Cet itérateur aura pour résultat le déréférencement de son itérateur de base, ce qui permettra de le déplacer ailleurs.

// assuming I understand your code, which I don't 
range.insert(range.end(), new_items.begin(), new_items.end()); 
while(range.size() >= x) 
{ 
    auto first = std::make_move_iterator(range.begin()); 
    auto last = std::make_move_iterator(range.begin() + x); 

    sub_ranges.push_back(std::vector<int>(first, last)); 
    range = std::vector<int>(range.begin() + x, range.end()); 
} 

EDIT: Et comme vous l'avez trouvé, il y a une correspondance entre std::move() et make_move_iterator:

std::move(first, last, out); // is the same as 
std::copy(std::make_move_iterator(first), std::make_move_iterator(last), out); 

Alors que vous trouvez plus propre est à vous. (Le premier, pour moi.)

3

Une question: avez-vous déjà entendu parler du concept View. L'idée est que, au lieu de déplacer réellement les données, vous créez simplement une "vue" (modèle de proxy) qui va restreindre/changer votre perception de celui-ci.

Par exemple, pour une gamme, une mise en œuvre très simple serait:

template <typename Iterator> 
struct Range 
{ 
    Iterator mBegin, mEnd; 
}; 

Boost.Range propose une version de graisse, avec beaucoup de choses.

Les avantages sont nombreux dans ce cas. parmi lesquels:

  • Un seul vector, donc une meilleure localisation mémoire
  • La répartition est facile, et ne comporte pas de mouvement/copie des données

Voici le split avec cette méthode :

typedef Range<std::vector<int>::iterator> range_type; 

std::vector<range_type> split(std::vector<int> const& range, size_t x) 
{ 
    std::vector<range_type> subRanges; 
    for (std::vector<int>::iterator it = range.begin(), end = range.end(); 
     it != end; it += std::min(x, (size_t)std::distance(it,end))) 
    { 
    subRanges.push_back(range_type(it,end)); 
    } 
    return subRanges; 
} 

Bien sûr, cela ne fonctionne que si vous pouvez garder l'objet range autour.


En ce qui concerne votre algorithme original: l'utilisation d'une boucle while est faux ici, et vous oblige à utiliser beaucoup plus move s que nécessaire. La boucle for que j'ai fabriquée devrait être beaucoup mieux à cet égard.