2017-07-07 68 views
10

Tôi viết một lớp CustomVector, nội bộ lưu trữ dữ liệu sử dụng một vector tiêu chuẩn:C++ Viết const vector với con trỏ để không const

template <class T> 
class CustomVector { 
    friend class CustomVector_ref<T>; 

    public: 
    ... 

    private: 
    std::vector<T> _data; 
}; 

Sau đó, để trích xuất subvectors từ CustomVector, tôi sử dụng một lớp lưu trữ con trỏ cho mỗi phần tử dữ liệu:

template <class T> 
class CustomVector_ref { 
    public: 
    //Returns the value stored in CustomVector 
    //and pointed-to by _data_ref 
    T& operator[] (size_t id) { return *_data_ref[id] } 
    const T& operator[] const (size_t id) { return *_data_ref[id] } 
    ... 
    private: 
    std::vector<T*> _data_ref; 
}; 

Bây giờ, để minh họa cho vấn đề của tôi nó là đủ để xem xét các costructor đơn giản xây dựng một tài liệu tham khảo cho tất cả các yếu tố của CustomVector

template<class T> 
CustomVector_ref<T>::CustomVector_ref(CustomVector<T>& cv) 
{ 
    for (T& el : cv._data) 
    _data_ref.push_back(&el); 
} 

đó làm việc tốt, nhưng nếu tôi có một CustomVector const, tôi cũng cần phải xác định các nhà xây dựng:

template<class T> 
CustomVector_ref<T>::CustomVector_ref(const CustomVector<T>& cv) 
{ 
    for (const T& el : cv._data) 
    _data_ref.push_back(const_cast<T*>(&el)); 
} 

đó làm việc quá, nhưng nếu đối tượng CustomVector_ref không khai báo là const, sau đó với các phi -const operator [] có thể ghi dữ liệu vào đối tượng CustomVector const.

const CustomVector<int> cv(...) //CostumVector is somehow constructed, 
           //that does not matter now 

std::cout<<cv[0]<<std::endl; //output 1 for example 

CustomVector_ref<int> cvr(cv) 

cvr[0] = 2; 

std::cout<<cv[0]<<std::endl; //now cv[0] stores 2 

Có thể tránh hành vi này?

tôi đã nhận thấy rằng điều này cũng xảy ra với vectơ tiêu chuẩn, ví dụ

const std::vector<int> v(1,1); 
std::vector<int*> vp; 

vp.push_back(const_cast<int*>(&v[0])); 

*vp[0] = 2; 

std::cout<<v[0]<<std::endl; // now v[0] stores 2, not 1 

Vì vậy, vì đây là tiêu chuẩn C++, tôi không bận tâm quá nhiều để sửa chữa CustomVector của tôi, nhưng nó sẽ được tốt đẹp để biết nếu có một giải pháp (không quá phức tạp).

+18

Bạn cần suy nghĩ về thực tế đơn giản rằng các thùng chứa thư viện chuẩn xác định hai lớp lặp khác nhau: 'iterator' và' const_iterator'. Có một lý do chính đáng cho điều đó, và bạn vừa phát hiện ra lý do đó là gì. Tình hình của bạn là hoàn toàn tương tự. Bạn cần triển khai hai tham chiếu khác nhau, một tham chiếu có thể thay đổi và 'const'. Đối với tín dụng bổ sung, tham chiếu có thể thay đổi sẽ được chuyển thành tham chiếu 'const', vì vậy nó có thể được chuyển đến các hàm lấy tham chiếu' const' làm đối số. –

+0

do dự để gắn cờ nó là lừa đảo, nhưng điều này được thực hiện chặt chẽ https://stackoverflow.com/questions/44882363/does-const-iterator-really-need-to-be-a-different-class-than-iterator – user463035818

+1

Điều gì về việc sử dụng 'CustomVector_ref '?Không có const_cast cần thiết theo cách này – geza

Trả lời

1

Tôi không chắc chắn chính xác những gì bạn đang cố gắng làm. Bạn có đang cố ngăn chặn sửa đổi vectơ ban đầu bất kể CustomVector_ref đã được khởi tạo với phiên bản vectơ const hoặc non-const của véc tơ không? Nếu vậy, bạn có thể làm điều này bằng cách đơn giản thay đổi kiểu trả về của operator[], như sau:

template <class T> 
class CustomVector_ref { 
    public: 
    ... 
    const T& operator[] (size_t id) { return *_data_ref[id] } 
    const T& operator[] const (size_t id) { return *_data_ref[id] } 
    ... 
}; 

Lưu ý rằng lưu trữ con trỏ vào vector ban đầu là nguy hiểm. Nếu kích thước của vectơ ban đầu thay đổi, tất cả các giá trị con trỏ của bạn có thể bị vô hiệu.

Nếu bạn muốn thay đổi hành vi của CustomVector_ref tuỳ thuộc vào việc nó được xây dựng với một phiên bản const hoặc non-const của vector gốc, bạn sẽ cần phải thay đổi chữ ký của người mẫu để có thể phân biệt giữa constnon-const phiên bản của vectơ gốc. Triển khai ví dụ:

#include <iostream> 
#include <vector> 

template <class T> 
class CustomVector_ref { 
public: 
    CustomVector_ref(T& orig_vector) : m_vector_ref(orig_vector) {} 

    auto& operator[] (size_t id) { return m_vector_ref[id]; } 
    const typename T::value_type& operator[] (size_t id) const { return m_vector_ref[id]; } 

    private: 
    T& m_vector_ref; 
}; 

int main(int argc, char* argv[]) { 
    std::vector<int> my_vec({1, 2, 3}); 
    std::cout << my_vec[0] << std::endl; 

    CustomVector_ref<std::vector<int>> cv_ref(my_vec); 
    cv_ref[0] = 2; // Assignment is ok; non-const cv_ref initialized with a non-const vector 
    std::cout << cv_ref[0] << std::endl; //now cv[0] stores 2 

    CustomVector_ref<const std::vector<int>> cv_cref(my_vec); 
    // cv_cref[0] = 2; // Compile error: assignment of read-only location 
    const_cast<int&>(cv_cref[0]) = 2; // Explicit override of const 
    std::cout << cv_cref[0] << std::endl; 

    const std::vector<int> my_const_vec({1, 2, 3}); 
    // CustomVector_ref<std::vector<int>> cv_cref2(my_const_vec); // Compile error; non-const initialization from const 
    CustomVector_ref<const std::vector<int>> cv_cref3(my_const_vec); // Ok, const initialization from const 
    // cv_cref3[0] = 2; // Compile error: assignment of read-only location 
    const_cast<int&>(cv_cref3[0]) = 2; // Explicit override of const 
    std::cout << cv_cref3[0] << std::endl; 

    return 0; 
} 
Các vấn đề liên quan