2013-04-05 7 views
8

W C++ Chciałbym przydzielić stały rozmiar (ale rozmiar określony w czasie wykonywania) std :: vector, a następnie napisać do elementów w tym wektorze. Jest to kod używam:Utwórz stały rozmiar std :: vector i napisz do elementów

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

Jednak to daje mi kompilator (MSVC 2010 Pro) błąd:

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

Moje rozumienie const jest to, że sprawia, że ​​wszystkie zmienne członkowskich stała klasy. Na przykład, następujący działa dobrze:

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 

Wydaje się zatem, że std :: vector propaguje const-ności pojemnika do elementów wektora, co wydaje się dziwne, bo gdybym chciał elementy do być const Użyłbym std::vector<const int>. To dlatego uderza mnie jako wadę std :: vector.

W każdym razie, w jaki sposób mogę utworzyć std :: vector, którego rozmiar nie może zostać zmieniony po konstrukcji, ale do którego elementów można zapisać?

+0

Najpierw utwórz zmienną "a", a następnie wykonaj ostatnią linię. –

+0

@GamesBrainiac: Wtedy 'a' nie jest odniesieniem. Błąd jest spowodowany posiadaniem zapisywalnego odniesienia do elementu w wektorze, który jest dokładnie tym, czego chcę. – user664303

+0

Powinieneś spróbować użyć 'std :: vector ' ... zobacz, co się stanie –

Odpowiedz

11

Nie jest to możliwe bez napisania własnej klasy opakowania. Jeśli chcesz użyć zwykłego std::vector, musisz polegać na samodyscyplinie, nie używając funkcji członkowskich, bezpośrednio lub pośrednio (np. Poprzez back_inserter).

Zauważ, że jest tam obecna propozycja dla dynamic arrays dla nowego standardu C++ 14:

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

Propozycja rzeczywiście pochodzi z implementacji referencyjnej, które można wykorzystać w swoim własnym kodzie (upewnij się, aby zmienić namespace std na coś innego na razie).

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 
+1

+1, tylko jak długo trwa próbka kodu. –

+0

@GamesBrainiac tnx! ale przykład kodu został skopiowany z propozycji, ale mimo to jest "gotowy do szufli". – TemplateRex

+0

Dziękujemy za opublikowanie implementacji dynarray. Poszukuję również odpowiedniego wyjaśnienia, dlaczego 'const std :: vector ' oznacza to samo co 'const std :: vector ', gdy może zamiast tego oznaczać dokładnie to, do czego proponuje się std :: dynarray, potrzeba nowego pojemnika. – user664303

1

Rzeczywisty błąd dlatego, zadeklarować wektor być stała, co oznacza, że ​​nie może zmienić zawartość.

Następnie, gdy próbujesz uzyskać niestałe odniesienie do pozycji w wektorze, kompilator mówi, że nie możesz tego zrobić, ponieważ wtedy możesz zmienić stałą wartość przechowywaną w wektorze.


chodzi o tworzenie wektor o rozmiarze, który może zostać ustalona w czasie wykonywania, ale nie zmienia rozmiar po wektor został stworzony, to trzeba stworzyć adapter kontenera. Zasadniczo musisz utworzyć opakowanie wokół innego kontenera, np. std::stack ma.

+0

const oznacza, że ​​nie mogę zmienić zmiennych składowych instancji wektora. Jednak pomyślałbym, że elementy wektora nie są członkami instancji, tylko że ich wskaźnik jest. Wskaźnik nie powinien wskazywać na elementy const, ponieważ nie używałem std :: vector . To dziwne, że nie mogę zmienić żywiołów. – user664303

+1

@ user664303 Tworząc wektor 'const', mówisz kompilatorowi, że _all_ wektora jest stałe, nie tylko rozmiar, ale także jego zawartość. –

+0

Oczywiście, w przypadku std :: vector, yes. Ale to nie jest standardowe zachowanie. Zobacz moje zaktualizowane pytanie. Dlaczego robi to std :: vector? Zwłaszcza mając na uwadze, że użyłbym typu const, gdybym chciał elementów const. Nikt jeszcze nie uzasadnił mi tego zachowania. – user664303

1

Bezpośrednią odpowiedzią jest to, że nie możesz tego zrobić: nie możesz zdefiniować wektora jako const, a następnie dodać do niego członków.

Jak zauważyli inni, nowy standard oferuje klasę macierzy, która jest prawdopodobnie bardziej odpowiednia do tego, co robisz.

Jeśli jesteś zainteresowany w stałej długości, najbardziej pokrewny sposób w wektorze może być zainteresowany jest reserve(), który ustawi vector<> do wielkości danego parametru, dzięki ekspansji wektor niepotrzebne.

Jeśli nie możesz użyć Std C++ 11, musisz utworzyć klasę opakowania, która nie pozwala na modyfikację wektora. Na przykład:

#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; 
    } 
} 

Mam nadzieję, że to pomoże.

+0

Nie dodaję członków. Wektor jest przypisany do prawidłowego rozmiaru konstrukcji. Nie ma problemu. Problem pojawia się, gdy chcę napisać do żywiołów. Ale tak, wygląda na to, że potrzebuję opakowania. – user664303

+0

@Baltasarq dość stary post, ale w każdym razie: w jaki sposób otulina powoduje, że rozmiar wektorowy jest stały? Można nazwać FinalVector.push_back tyle razy, ile potrzeba, aw tle wektor z przyjemnością zwiększyłby swój rozmiar, aby wypełnić wszystkie elementy. Może coś przeoczyłem, ale spodziewałbym się, że potrzeba trochę więcej kodu, aby naprawdę utrzymać rozmiar. – user463035818

+0

Masz rację, @ ​​tobi303. Wydaje się, że odpowiedź była tak niepopularna, że ​​nikt inny nie zdawał sobie sprawy (tak jak ja). Poprawiłem kod, więc teraz wektor ma naprawdę ustalony rozmiar. – Baltasarq

Powiązane problemy