2011-12-17 1 views
1

У меня проблема с инкубационными контейнерами.const list, доступ к неконтекстному элементу

Один из моих классов имеет назойливый список некоторых объектов, жизнь которых строго управляется им. Сами объекты должны быть изменены пользователями класса, но они не должны изменять сам список. Вот почему я предоставляю доступ к списку только через функцию getList, которая возвращает const-версию интрузивного списка.

Проблема с const intrusive list заключается в том, что элементы также оказываются const, когда вы пытаетесь выполнить итерацию через них. Но пользователи должны иметь возможность перебирать и изменять элементы.

Я не хочу оставлять отдельный список указателей, чтобы дать пользователям, потому что это лишит вас одного из самых больших преимуществ использования интрузивных контейнеров. А именно, возможность удалять элементы из контейнера в постоянное время, в то время как единственное, что у вас есть, - это указатель на элемент.

Было бы грустно приносить неконвертную версию моего списка только из-за ограничения C++. Таким образом, возникает вопрос: существует ли специальная версия const интрузивных контейнеров, которая волшебным образом позволяет изменять элементы, не допуская каких-либо изменений в самом списке?

ответ

0

ОК, я разработал комплексное решение этой проблемы. Решение Энди приятно, если вам не нужно выполнять итерацию по элементам эффективным образом. Но мне нужно что-то семантически эквивалентное const std :: list. Возможно, это чересчур, но производительность по сравнению с оптимизацией практически не изменилась:

Решение состоит в том, чтобы частным образом расширить список навязчивого списка с помощью класса ConstList, который предоставляет достаточно ровный объем, чтобы позволить BOOST_FOREACH выполнять итерацию, но не вносить никаких изменений кто угодно. Я переместил список из элемента в дочерний класс, так что объект элемента не может быть использован для изменения списка. Мы храним дочерний класс с помощью hook, но наши итераторы возвращают ссылки на класс item. Я закодировал это решение на два шаблонных класса для простого применения к любому классу предметов.

Я создал файл заголовка с классом ConstList и классом HookedItem, а затем test.cpp, используемый для тестирования и тестирования. Вы увидите, что наш класс ConstList имеет равную производительность при повторении.

Он работает совершенно чисто, и код пользователя также остается чистым. Тогда это задает вопрос: почему это уже не в boost ????!?!?

Вы можете использовать следующий код для любой цели :)

PS: У меня был момент откровения, пока придумываешь с этим решением: «сопзИте» не что иное, как синтаксический сахар для особого случая от того, что вы уже можете достичь с помощью правильной иерархии классов. Это правда, или я слишком обобщаю?

------------------ ConstList.h -----------------------

#include <boost/intrusive/list_hook.hpp> 

template < typename T> 
struct type_wrapper{ typedef T type;}; 

template<class listType, class owner, class item> 
class ConstList: private listType { 
    friend class type_wrapper<owner>::type; 
public: 
    class iterator { 
     typename listType::iterator it; 
    public: 
     typedef std::forward_iterator_tag iterator_category; 
     typedef item value_type; 
     typedef int difference_type; 
     typedef item* pointer; 
     typedef item& reference; 
     template<class T> 
     iterator(const T it): it(it){} 
     bool operator==(iterator & otherIt) {return it==otherIt.it;} 
     iterator & operator++() { 
      it++; 
      return *this; 
     } 
     item & operator*() { 
      return *it; 
     } 
    }; 
    iterator begin() { 
     return iterator(listType::begin()); 
    } 

    iterator end() { 
     return iterator(listType::end()); 
    } 
}; 

template<class item, class owner, class hooktype> 
class HookedItem: public item { 
    friend class type_wrapper<owner>::type; 
public: 
    hooktype hook_; 
    typedef boost::intrusive::member_hook<HookedItem, hooktype, &HookedItem::hook_> MemberHookOption; 
private: 
    template<class Arg1, class Arg2> 
    HookedItem(Arg1 &arg1, Arg2 &arg2): item(arg1, arg2){} 
}; 

------------------ испытания.cpp -----------------------

#include<cstdio> 
#include<boost/checked_delete.hpp> 
#include<ConstList.h> 
#include<boost/intrusive/list.hpp> 
#include<boost/foreach.hpp> 

using namespace boost::intrusive; 

class myOwner; 
class myItem { 
public: 
    int a,b; //arbitrary members 
    myItem(int a, int b): a(a), b(b){}; 
}; 

typedef HookedItem<myItem,myOwner,list_member_hook<> > myHookedItem; 
typedef list<myHookedItem, typename myHookedItem::MemberHookOption> myItemList; 
typedef ConstList<myItemList,myOwner,myItem> constItemList; 

class myOwner { 
public: 
    constItemList constList; 
    myItemList & nonConstList; 
    myOwner(): nonConstList(constList) {} 
    constItemList & getItems() { return constList;} 
    myItem * generateItem(int a, int b) { 
     myHookedItem * newItem = new myHookedItem(a,b); 
     nonConstList.push_back(*newItem); 
     return newItem; 
    } 
    ~myOwner() {nonConstList.clear_and_dispose(boost::checked_delete<myHookedItem>);} 
}; 


int main(int argc, char **argv) { 
    myOwner owner; 
    int avoidOptimization=0; 
    for(int i=0; i<1000000; i++) { 
     owner.generateItem(i,i); 
    } 


    clock_t start = clock(); 
    for(int i=0; i<1000; i++) 
     BOOST_FOREACH(myItem & item, owner.constList) 
      avoidOptimization+=item.a; 
    printf ("%f\n", ((double)clock() - start)/CLOCKS_PER_SEC); 


    start = clock(); 
    for(int i=0; i<1000; i++) 
     BOOST_FOREACH(myHookedItem & item, owner.nonConstList) 
      avoidOptimization+=item.a; 
    printf ("%f\n", ((double)clock() - start)/CLOCKS_PER_SEC); 


    printf ("%d",avoidOptimization); 
    return 0; 
} 

------------ Консольный выход ----- ------------

4.690000 
4.700000 
1764472320 
0

Вам не нужно возвращать список, дать доступ к отдельным пунктам по ссылке

+0

Это действительно хороший ответ, я перехожу из списка указателей на список навязчивого списка, поэтому мой разум застрял в возвращении списка , Одна из проблем, которые возникнут в результате этого решения, заключается в том, что пользователи смогут изменять список, используя сами неконстантные элементы. Есть ли решение? – enobayram

+0

ОК, я подумал о следующем решении о возможности манипулирования списком через элементы: у меня будет класс «itemBase», который не имеет списка и перехватывает экземпляры этого, ссылаясь на пользователей , В то время как список моего класса будет содержать объекты класса «item», который ничего не делает, кроме наследования от itemBase и перехвата списка. Это имеет смысл, или я что-то пропускаю? – enobayram

+0

Да, звучит нормально. есть и другой вариант (я предполагаю, что вы используете «базовые крючки»): выведите конфиденциально из 'list_base_hook' и объявите' boost :: intrusive :: list 'как друга в' Item'. –