2013-04-05 41 views
8

Trong C++, tôi muốn phân bổ kích thước cố định (nhưng kích thước được xác định khi chạy) std :: vector rồi ghi vào các phần tử trong vectơ này. Đây là mã tôi đang sử dụng:Tạo một kích thước cố định std :: vector và ghi vào các phần tử

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

Tuy nhiên, điều này mang lại cho tôi một trình biên dịch (MSVC 2010 Pro) lỗi:

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

sự hiểu biết của tôi về const là nó làm cho tất cả các biến thành viên của một hằng số lớp. Ví dụ, sau đây hoạt động tốt:

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 

Nó sẽ do đó dường như rằng std :: vector truyền const-Ness của các container để các yếu tố của véc tơ, mà có vẻ kỳ lạ bởi vì nếu tôi đã muốn các yếu tố để be const Tôi đã sử dụng std::vector<const int>. Điều này do đó tấn công tôi như một thiếu sót của std :: vector.

Trong mọi trường hợp, làm cách nào để tạo một std :: vector có kích thước không thể thay đổi sau khi xây dựng, nhưng có thể ghi phần tử nào?

+0

Tạo biến 'a' trước, sau đó thực hiện dòng cuối cùng của bạn. –

+0

@GamesBrainiac: Sau đó 'a' không phải là tham chiếu. Lỗi này là do có một tham chiếu có thể ghi tới một phần tử trong vectơ, đó chính xác là những gì tôi muốn. – user664303

+0

Bạn nên thử sử dụng 'std :: vector ' ... xem điều gì xảy ra –

Trả lời

11

Điều này là không thể nếu không viết lớp trình bao bọc của riêng bạn. Nếu bạn muốn sử dụng đồng bằng std::vector, bạn phải dựa vào kỷ luật tự giác bằng cách không sử dụng các chức năng thành viên insert(), push_back() hoặc emplace_back(), trực tiếp hoặc gián tiếp (ví dụ: thông qua back_inserter).

Lưu ý rằng có một đề xuất hiện tại cho dynamic arrays cho cái mới C++ 14 Tiêu chuẩn:

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

Đề xuất này thực sự đi kèm với một tài liệu tham khảo thực hiện mà bạn có thể sử dụng trong mã của riêng bạn (chắc chắn phải thay đổi namespace std vào một cái gì đó khác trong thời gian này).

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 chỉ trong khoảng thời gian mẫu mã. –

+0

@GamesBrainiac tnx! nhưng ví dụ mã được sao chép từ đề xuất, nhưng nó vẫn là "xẻng sẵn sàng". – TemplateRex

+0

Cảm ơn bạn đã đăng triển khai dynarray. Tôi cũng đang tìm kiếm một lời giải thích đầy đủ về lý do tại sao 'const std :: vector ' có nghĩa giống như 'const std :: vector ', khi thay vào đó nó có nghĩa là chính xác những gì std :: dynarray được đề xuất, forgoing sự cần thiết cho một container mới. – user664303

1

Các lỗi thực tế là bởi vì bạn khai báo các vector được liên tục, có nghĩa là bạn không bao giờ có thể thay đổi nội dung. Sau đó, khi bạn cố gắng lấy một tham chiếu không liên tục đến một mục trong vector, trình biên dịch cho bạn biết rằng bạn không thể làm điều đó, bởi vì sau đó bạn có thể thay đổi giá trị không đổi được lưu trữ trong vec-tơ.


Để tạo vectơ có kích thước có thể cố định khi chạy, nhưng không thay đổi kích thước sau khi tạo vectơ, thì bạn phải tạo bộ điều hợp vùng chứa. Về cơ bản, bạn phải tạo trình bao bọc xung quanh vùng chứa khác, giống như ví dụ: std::stack.

+0

const nghĩa là tôi không thể thay đổi biến thành viên của cá thể vectơ. Tuy nhiên, tôi đã nghĩ rằng các phần tử của vectơ không phải là thành viên của cá thể, mà chỉ là con trỏ của chúng. Con trỏ không nên trỏ đến các phần tử const như tôi đã không sử dụng std :: vector . Vì vậy, nó đình công tôi như là lẻ mà tôi không thể thay đổi các yếu tố. – user664303

+1

@ user664303 Bằng cách làm cho vector 'const' bạn nói với trình biên dịch rằng _all_ của vectơ là hằng số, không chỉ kích thước mà còn cả nội dung của nó. –

+0

Rõ ràng, trong trường hợp std :: vector, yes. Nhưng đây không phải là hành vi tiêu chuẩn. Xem câu hỏi được cập nhật của tôi. Vậy tại sao std :: vector làm điều này? Đặc biệt nhớ rằng tôi đã sử dụng một loại const nếu tôi muốn các yếu tố const. Chưa có ai chứng minh đầy đủ hành vi với tôi. – user664303

1

Câu trả lời trực tiếp là bạn không thể làm điều đó: bạn không thể xác định vectơ là const và sau đó thêm thành viên vào nó.

Như những người khác đã chỉ ra, tiêu chuẩn mới cung cấp lớp mảng, có lẽ phù hợp hơn cho những gì bạn đang làm.

Nếu bạn quan tâm đến độ dài cố định, phương pháp liên quan nhất trong vector bạn có thể quan tâm là reserve(), sẽ đặt vector<> thành kích thước của tham số đã cho, không cần mở rộng vectơ.

Nếu bạn không thể sử dụng Std C++ 11, thì bạn cần tạo lớp bao bọc không cho phép bạn sửa đổi vectơ. Ví dụ:

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

Hy vọng điều này sẽ hữu ích.

+0

Tôi không thêm thành viên. Vectơ được phân bổ là kích thước chính xác trong xây dựng. Không sao cả. Vấn đề nảy sinh khi tôi muốn viết cho các yếu tố. Nhưng có, có vẻ như tôi sẽ cần một wrapper. – user664303

+0

@Baltasarq khá cũ, nhưng dù sao đi nữa: làm thế nào để bạn wrapper làm cho kích thước vector cố định? Người ta có thể gọi FinalVector.push_back nhiều lần như mong muốn và trong nền vector sẽ vui vẻ tăng kích thước của nó để điền vào tất cả các yếu tố. Có lẽ tôi đang nhìn một cái gì đó, nhưng tôi hy vọng rằng một số mã hơn là cần thiết để thực sự giữ kích thước cố định. – user463035818

+0

Bạn nói đúng, @ tobi303. Dường như câu trả lời không được ưa chuộng đến nỗi không ai khác nhận ra (yep, kể cả tôi). Tôi đã sửa mã để bây giờ vector thực sự là kích thước cố định. – Baltasarq

Các vấn đề liên quan