2013-09-19 1 views
2

У меня возникла странная проблема. У меня есть vector<pair<bool, int>>, из которого мне нужно читать (и, возможно, писать) только векторные элементы, для которых логическое значение пары истинно. Для этого я использую фильтр расширенного диапазона и обратные адаптеры.C++: итератор с расширенным диапазоном, указывающий на неправильный элемент

Тем не менее, я заметил, что порядок адаптеров, т. Е. Использует ли меня обратный | фильтруют или фильтруют | обратный результат дает разные результаты. Фактически, когда я использую фильтр | обратный, тогда, когда я использую итератор для преобразованного диапазона, чтобы изменить логическое значение пары, тогда итератор после изменения указывает на другой векторный элемент. Этого не происходит, когда я использую обратное | фильтруют. Ниже приведен код, демонстрирующий проблему. Любые идеи относительно того, почему это происходит, очень ценятся!

#include <boost/range/adaptors.hpp> 
#include <vector> 
#include <utility> 
#include <iostream> 

using namespace boost::adaptors; 

using container_type = std::vector<std::pair<bool,int>>; 

struct to_include { 
    bool operator()(const std::pair<bool,int>& x) { 
    return x.first; 
    } 
}; 

int main() { 
    container_type container; 

    /* element0: 1, 1 */ 
    /* element1: 1, 2 */ 
    /* element2: 1, 3 */ 
    for(size_t i=0; i!=3; ++i) container.push_back(std::make_pair(true, i+1)); 
    container_type container_cpy = container; 

    /* filter and then reverse */ 
    auto fr = container | filtered(to_include()) | reversed; 
    auto fr_it1 = fr.begin(); 
    auto fr_it2 = std::next(fr_it1); 
    fr_it2->first = false; 

    std::cout << "FILTER AND THEN REVERSE\n"; 
    std::cout << fr_it2->first << " " << fr_it2->second << '\n'; /* prints (1,1) instead of (0,2) */ 

    /* reverse and then filter */ 
    auto rf = container_cpy | reversed | filtered(to_include()); 
    auto rf_it1 = rf.begin(); 
    auto rf_it2 = std::next(rf_it1); 
    rf_it2->first = false; 

    std::cout << "\nREVERSE AND THEN FILTER\n"; 
    std::cout << rf_it2->first << " " << rf_it2->second << '\n'; /* prints (0,2) */ 

    return 0; 
} 
+0

Я, по общему признанию, не имел опыта с адаптерами дальнего радиуса действия, но для меня было бы разумно, если 'fr' был своего рода * обратным * диапазоном, так что' fr_it1' должен быть 'rbegin' вместо' begin'. – us2012

+0

@ us2012: спасибо за помощь. 'fr' - это обратный диапазон (на самом деле это измененный диапазон фильтров), что означает, что его итератор' begin' указывает на последний элемент исходного контейнера (т.е. до реверсии). – linuxfever

ответ

1

Это тонкий вопрос. Дело здесь в том, что после изменения элемента, на который указывает fr_it2, вы также неявно изменяете fr_it1, потому что fr - это ленивый вид в оригинальном диапазоне. Это означает, что фильтр transformed необходимо переоценить. Это очень неинтуитивное свойство, потому что для нетерпеливых диапазонов STL модификации с помощью итераторов не изменяют самих итераторов, но для ленивых диапазонов это уже не так!

Фактически, если вы напечатаете все диапазоны fr и rf, используя «свежие» итераторы, вы увидите, что их содержимое на самом деле то же самое.

fr_it2->first = false; 
for (auto e : fr) std::cout << e.first << e.second << ";"; // prints 13;11 
... 
rf_it2->first = false; 
for (auto e : rf) std::cout << e.first << e.second << ";"; // prints 13;11 

Live Example 1. Так что на самом деле средний элемент действительно удален!

Я думаю, что вы не должны изменять элементы через итераторы в adapated диапазоне, а через итераторы в ваш первичный контейнер, например:

auto fr_it1 = container.begin(); 
... 
auto rf_it1 = container_cpy.begin(); 

Live Example 2. Если вы это сделаете, вы получите согласованные результаты, которые показывают «0 2» для обоих подходов.

 Смежные вопросы

  • Нет связанных вопросов^_^