Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a fixed size std::vector and write to the elements

In C++ I wish to allocate a fixed-size (but size determined at runtime) std::vector then write to the elements in this vector. This is the code I am using:

int b = 30;
const std::vector<int> test(b);
int &a = test[3];

However, this gives me a compiler (MSVC 2010 Pro) error:

error C2440: 'initializing' : cannot convert from 'const int' to 'int &'. Conversion loses qualifiers.

My understanding of const is that it makes all of the member variables of a class constant. For example, the following works fine:

class myvec
{
public:
    myvec(int num) : ptr_m(new int[num]) {};
    ~myvec() { delete ptr_m; }
    void resize(int num) { delete ptr_m; ptr_m = new int[num]; }
    int & operator[] (int i) const { return ptr_m[i]; }
    int *ptr_m;
};

const myvec test(30);
int &a = test[3]; // This is fine, as desired
test.resize(10); // Error here, as expected

It would therefore seem that std::vector propagates the const-ness of the container to the elements of the vector, which seems odd because if I had wanted the elements to be const I would have used std::vector<const int>. This therefore strikes me as a shortcoming of std::vector.

In any case, how can I create a std::vector whose size cannot be changed after construction, but whose elements can be written to?

like image 625
user664303 Avatar asked Apr 05 '13 11:04

user664303


2 Answers

This is not possible without writing your own wrapper class. If you want to use a plain std::vector, you have to rely on self-discipline by not using the member functions insert(), push_back() or emplace_back(), either directly or indirectly (e.g. via a back_inserter).

Note that there is a current proposal for dynamic arrays for the new C++14 Standard:

[...] we propose to define a new facility for arrays where the number of elements is bound at construction. We call these dynamic arrays, dynarray.

The proposal actually comes with a reference implementation that you can use in your own code (make sure to change namespace std into something else for the time being).

namespace std {
template< class T >
struct dynarray
{
    // types:
    typedef       T                               value_type;
    typedef       T&                              reference;
    typedef const T&                              const_reference;
    typedef       T*                              iterator;
    typedef const T*                              const_iterator;
    typedef std::reverse_iterator<iterator>       reverse_iterator;
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
    typedef size_t                                size_type;
    typedef ptrdiff_t                             difference_type;

    // fields:
private:
    T*        store;
    size_type count;

    // helper functions:
    void check(size_type n)
        { if ( n >= count ) throw out_of_range("dynarray"); }
    T* alloc(size_type n)
        { if ( n > std::numeric_limits<size_type>::max()/sizeof(T) )
              throw std::bad_array_length();
          return reinterpret_cast<T*>( new char[ n*sizeof(T) ] ); }

public:
    // construct and destruct:
    dynarray() = delete;
    const dynarray operator=(const dynarray&) = delete;

    explicit dynarray(size_type c)
        : store( alloc( c ) ), count( c )
        { size_type i;
          try {
              for ( size_type i = 0; i < count; ++i )
                  new (store+i) T;
          } catch ( ... ) {
              for ( ; i > 0; --i )
                 (store+(i-1))->~T();
              throw;
          } }

    dynarray(const dynarray& d)
        : store( alloc( d.count ) ), count( d.count )
        { try { uninitialized_copy( d.begin(), d.end(), begin() ); }
          catch ( ... ) { delete store; throw; } }

    ~dynarray()
        { for ( size_type i = 0; i < count; ++i )
              (store+i)->~T();
          delete[] store; }

    // iterators:
    iterator       begin()        { return store; }
    const_iterator begin()  const { return store; }
    const_iterator cbegin() const { return store; }
    iterator       end()          { return store + count; }
    const_iterator end()    const { return store + count; }
    const_iterator cend()   const { return store + count; }

    reverse_iterator       rbegin()       
        { return reverse_iterator(end()); }
    const_reverse_iterator rbegin()  const
        { return reverse_iterator(end()); }
    reverse_iterator       rend()         
        { return reverse_iterator(begin()); }
    const_reverse_iterator rend()    const
        { return reverse_iterator(begin()); }

    // capacity:
    size_type size()     const { return count; }
    size_type max_size() const { return count; }
    bool      empty()    const { return count == 0; }

    // element access:
    reference       operator[](size_type n)       { return store[n]; }
    const_reference operator[](size_type n) const { return store[n]; }

    reference       front()       { return store[0]; }
    const_reference front() const { return store[0]; }
    reference       back()        { return store[count-1]; }
    const_reference back()  const { return store[count-1]; }

    const_reference at(size_type n) const { check(n); return store[n]; }
    reference       at(size_type n)       { check(n); return store[n]; }

    // data access:
    T*       data()       { return store; }
    const T* data() const { return store; }
};

} // namespace std
like image 66
TemplateRex Avatar answered Sep 19 '22 05:09

TemplateRex


The actual error is because you declare the vector to be constant, meaning you can never change the contents.

Then when you try to get a non-constant reference to an entry in the vector, the compiler tells you that you can't do that, because then you could change the constant value stored in the vector.


As for creating a vector with a size that can be fixed at runtime, but not change size after the vector has been created, then you have to create a container adaptor. Basically you have to create a wrapper around another container, just like e.g. std::stack does.

like image 22
Some programmer dude Avatar answered Sep 23 '22 05:09

Some programmer dude