список констант, неконстантный доступ к элементу - PullRequest
1 голос
/ 17 декабря 2011

У меня проблема с форсированными навязчивыми контейнерами.

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

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

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

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

Ответы [ 2 ]

0 голосов
/ 18 декабря 2011

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

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

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

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

Не стесняйтесь использовать следующий код для любых целей:)

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

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

------------------ tests.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 голосов
/ 17 декабря 2011

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

...