Как должен быть реализован stl-подобный контейнер с ограниченным размером? - PullRequest
3 голосов
/ 25 августа 2010

Во время рефакторинга я хотел изменить массив, в котором записи добавляются в std :: vector, но для совместимости (постоянство, понижение рейтинга, ...) ему все равно нужно иметь верхний предел.
Что такоелучший способ (элегантный, stl-подобный, ограниченный дополнительный код) иметь stl-подобный контейнер, который ограничен по размеру, так что вы знаете, что вставка записи не удалась?

Edit :
Чтобы уточнить: я хотел бы stl-подобный контейнер, который начинается пустым, который вы можете заполнить записями и, возможно, удалить записи, и который перебирает заполненные записи, но который не позволяет вставить болеенапример, 50 записей, так что почти как последовательное противопоставление, но с верхним пределом.

Ответы [ 6 ]

5 голосов
/ 25 августа 2010

Простым решением будет инкапсуляция вектора внутри вашего собственного контейнера ограниченного размера. Вы можете использовать частную композицию или частное наследование - отметьте, что модели частного наследования реализованы в терминах и не имеют некоторых недостатков публичного наследования.

РЕДАКТИРОВАТЬ : эскиз решения с частным наследованием

template <typename T, unsigned int N>
class fixed_vector : std::vector<T>
{
    typedef std::vector<T> vector_type;
public:
    typedef typename vector_type::reference reference;
    typedef typename vector_type::const_reference const_reference;
    typedef typename vector_type::iterator iterator;
    typedef typename vector_type::const_iterator const_iterator;
    typedef typename vector_type::value_type value_type;
    typedef typename vector_type::size_type size_type;

    fixed_vector() : vector_type() {}
    fixed_vector( size_type size, value_type const & value = value_type() )
       : vector_type(size,value)
    {}      

    void push_back( value_type v ) {
        ensure_can_grow();
        vector_type::push_back( v );
    }
    iterator insert( iterator position, value_type const & v ) {
        ensure_can_grow();
        vector_type::insert( position, v );
    }
    void reserve( size_type size ) {
        if ( size > N ) throw std::invalid_argument();
        vector_type::reserve( size );
    }
    size_type capacity() const {
        // In case the default implementation acquires by default 
        // more than N elements, or the vector grows to a higher capacity
        return std::min( vector_type::capacity(), N );
    }
    // provide other insert methods if required, with the same pattern
    using vector_type::begin;
    using vector_type::end;
    using vector_type::operator[];
    using vector_type::erase;
    using vector_type::size;
    using vector_type::empty;
private:
    void ensure_can_grow() const {
        // probably a different exception would make sense here: 
        if ( this->size() == N ) throw std::bad_alloc();
    }
};

Там довольно много размахивают руками ... std::vector Принимайте больше аргументов, которые можно добавить к фасаду. Если вам нужны какие-либо другие методы или typedef, вы можете просто перенести их в область действия с помощью объявления using, переопределить typedef или внедрить адаптер с вашим конкретным тестом.

Кроме того, в этой реализации размер является постоянной времени компиляции, но было бы действительно просто изменить ее в параметр конструктора.

3 голосов
/ 25 августа 2010

Настройте векторный класс для наложения верхнего предела. Возможно, вы можете открыть новый API, который будет проверять размер по верхнему пределу и возвращать значение false, если оно превышает, в противном случае вызовите обычный метод вставки.

2 голосов
/ 25 августа 2010

Взгляните на эту static_vector реализацию , которую я нашел некоторое время назад. Я думаю, что это именно то, что вы хотите.

Он распространяется по очень либеральной форсированной лицензии, так что вы можете делать с ней практически все.

2 голосов
/ 25 августа 2010

Вы можете создать собственный распределитель (например, полученный из std::allocator), который отказывается выделять массив больше заданного размера.

Обратите внимание, что вам нужно вызвать reserve( vector_max ) для получающегося объекта перед добавлением к нему объектов. Я регистрирую дефект в отношении стандарта C ++, так как требование должно быть ненужным (и оно есть в последних версиях GCC).

template< typename T, size_t N >
struct limited_alloc : std::allocator< T > {
    size_t max_size() const { return N; }
    typename std::allocator<T>::pointer allocate( size_t n ) {
        if ( n < N ) return std::allocator<T>::allocate( n );
        throw std::length_error( "array too large" );
    }

    limited_alloc() {} // silly cruft for standard requirements:
    template< typename T2 >
    limited_alloc( limited_alloc<T2,N> const & ) {}
    template< typename T2 >
    struct rebind { typedef limited_alloc<T2,N> other; };
};

enum { vector_max = 40 };

template< typename T >
struct limited_vector {
    typedef std::vector< T, limited_alloc< T, vector_max > > type;
};

void f() {
    limited_vector< int >::type x;
    x.reserve( vector_max );
    x.assign( vector_max + 1, 3 ); // throws.
}
1 голос
/ 25 августа 2010

Посмотрите на boost :: array

Редактировать: для добавления / удаления boost :: option можно использовать как тип элемента boost :: array.

1 голос
/ 25 августа 2010

Взгляните на Boost.Array

В качестве замены обычных массивов STL предоставляет класс std :: vector. Однако std :: vector <> обеспечивает семантику динамических массивов. Таким образом, он управляет данными, чтобы иметь возможность изменять количество элементов. Это приводит к некоторым накладным расходам в случае, если нужны только массивы со статическим размером.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...