Pertanyaan Buat ukuran tetap std :: vector dan tulis ke elemen


Dalam C ++ saya ingin mengalokasikan ukuran tetap (tetapi ukuran yang ditentukan saat runtime) std :: vector kemudian tulis ke elemen dalam vektor ini. Ini adalah kode yang saya gunakan:

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

Namun, ini memberi saya kesalahan kompiler (MSVC 2010 Pro):

error C2440: 'menginisialisasi': tidak dapat mengkonversi dari 'const int' menjadi 'int &'. Konversi kehilangan kualifikasi.

Pemahaman saya tentang const adalah ia membuat semua variabel anggota dari sebuah konstanta kelas. Misalnya, hal berikut berfungsi dengan baik:

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

Oleh karena itu akan terlihat bahwa std :: vector menyebarkan const-ness dari wadah ke elemen vektor, yang kelihatannya aneh karena jika saya ingin elemen menjadi const, saya akan menggunakan std::vector<const int>. Ini karena itu menurut saya sebagai kekurangan std :: vector.

Dalam kasus apa pun, bagaimana saya dapat membuat std :: vector yang ukurannya tidak dapat diubah setelah konstruksi, tetapi elemen-elemennya dapat ditulis?


10
2018-04-05 11:09


asal


Jawaban:


Ini tidak mungkin tanpa menulis kelas pembungkus Anda sendiri. Jika Anda ingin menggunakan dataran std::vector, Anda harus mengandalkan disiplin diri dengan tidak menggunakan fungsi anggota insert(), push_back() atau emplace_back(), baik secara langsung atau tidak langsung (mis. via a back_inserter).

Perhatikan bahwa ada proposal saat ini untuk array dinamis untuk Standar C ++ 14 yang baru:

[...] kami mengusulkan untuk menentukan fasilitas baru untuk array di mana jumlah   elemen terikat pada konstruksi. Kami menyebutnya array dinamis,   dynarray.

Proposal ini sebenarnya dilengkapi dengan implementasi referensi yang dapat Anda gunakan dalam kode Anda sendiri (pastikan untuk berubah namespace std menjadi sesuatu yang lain untuk sementara waktu).

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

12
2018-04-05 11:12



Kesalahan sebenarnya adalah karena Anda menyatakan vektornya konstan, artinya Anda tidak pernah dapat mengubah isinya.

Kemudian ketika Anda mencoba untuk mendapatkan referensi non-konstan ke entri dalam vektor, kompilator memberitahu Anda bahwa Anda tidak dapat melakukan itu, karena Anda dapat mengubah nilai konstan yang tersimpan dalam vektor.


Sedangkan untuk membuat vektor dengan ukuran yang dapat diperbaiki pada saat runtime, tetapi tidak mengubah ukuran setelah vektor telah dibuat, maka Anda harus membuat adaptor kontainer. Pada dasarnya Anda harus membuat pembungkus di sekitar penampung lain, seperti misalnya std::stack tidak.


2
2018-04-05 11:13



Jawaban langsungnya adalah Anda tidak dapat melakukannya: Anda tidak dapat mendefinisikan vektor sebagai const dan kemudian menambahkan anggotanya.

Seperti yang orang lain tunjukkan, standar baru menawarkan kelas array, yang mungkin lebih cocok untuk apa yang Anda lakukan.

Jika Anda tertarik dengan panjang tetap, metode yang paling terkait dalam vektor yang dapat Anda minati adalah reserve(), yang akan mengatur vector<> untuk ukuran parameter yang diberikan, membuat ekspansi vektor tidak perlu.

Jika Anda tidak dapat menggunakan Std C ++ 11, maka Anda perlu membuat kelas pembungkus yang tidak memungkinkan Anda memodifikasi vektor. Sebagai contoh:

#include <vector>
#include <iostream>
#include <exception>
#include <stdexcept>
using namespace std;

template <typename T>
class FinalVector {
public:
    FinalVector(unsigned int size)
        { v.reserve( size ); }
    const T &at(unsigned int i) const
        { return v.at( i ); }
    T &at(unsigned int i)
        { return v.at( i ); }
    T &operator[](unsigned int i)
        { return at( i ); }
    const T &operator[](unsigned int i) const
        { return at( i ); }
    void push_back(const T &x);
    size_t size() const
        { return v.size(); }
    size_t capacity() const
        { return v.size(); }
private:
    std::vector<T> v;
};

template<typename T>
void FinalVector<T>::push_back(const T &x)
{
    if ( v.size() < v.capacity() ) {
        v.push_back( x );
    } else {
        throw runtime_error( "vector size exceeded" );
    }
}

int main()
{
    FinalVector<int> v( 3 );

    v.push_back( 1 );
    v.push_back( 2 );
    v.push_back( 3 );

    for(size_t i = 0; i < v.size(); ++i) {
        cout << v[ i ] << endl;
    }
}

Semoga ini membantu.


1
2018-04-05 11:24