2017-07-07 95 views
10

Piszę klasę CustomVector wewnętrznie przechowywania danych za pomocą standardowego Vector:C++ Pisanie const wektory z wskazówki dla non const

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

    public: 
    ... 

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

Następnie, w celu wydobycia podwektorach z CustomVector używam klasę przechowującą wskaźniki do każdego elementu danych:

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

teraz, aby zilustrować mój problem, wystarczy rozważyć prosty costructor budynek odniesienie do wszystkich elementów CustomVector

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

To działa dobrze, ale jeśli mam const CustomVector, muszę również zdefiniować konstruktor:

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

który działa zbyt, ale jeśli obiekt CustomVector_ref nie jest zadeklarowane jako const, a następnie non -const operator [] możliwe jest zapisywanie danych w stałym obiekcie CustomVector.

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 

Można tego uniknąć?

Zauważyłem, że to dzieje się również ze standardowymi wektorami, np

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 

Tak, ponieważ jest to standard C++, nie przejmuj się zbytnio, aby naprawić mój CustomVector, ale byłoby miło wiedzieć jeśli istnieje (niezbyt skomplikowane) rozwiązanie.

+18

Należy się zastanowić nad prostym faktem, że standardowe pojemniki biblioteczne definiują dwie różne klasy iteratora: 'iterator' i' const_iterator'. Jest ku temu dobry powód i właśnie odkryłeś, jaki jest ten powód. Twoja sytuacja jest zupełnie analogiczna. Musisz zaimplementować dwie różne referencje, zmienną i referencję 'const'. Aby uzyskać dodatkowe punkty, zmienne referencje powinny być rzutowane na referencje 'const', więc można je przekazać do funkcji, które pobierają odwołania' const' jako argumenty. –

+0

Wahając się oznaczyć to jako dupe, ale jest to bardzo realne https://stackoverflow.com/questions/44882363/does-const-iterator-really-need-to-be-a-different-than-iterator – user463035818

+1

Co o użyciu 'CustomVector_ref '?Żadna const_cast nie jest potrzebna w ten sposób. – geza

Odpowiedz

1

Nie jestem dokładnie pewien, co próbujesz zrobić. Czy próbujesz zapobiec modyfikacji oryginalnego wektora, niezależnie od tego, czy CustomVector_ref został zainicjowany za pomocą wersji const lub non-const? Jeśli tak, można to zrobić poprzez zmianę typu zwracanej operator[], co następuje:

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] } 
    ... 
}; 

Zauważ, że przechowywania wskaźniki do oryginalnego wektora jest niebezpieczne. Jeśli wielkość oryginalnego wektora zmieni się, wszystkie wartości wskaźnika mogą zostać unieważnione.

Jeśli chcesz zmienić zachowanie CustomVector_ref w zależności od tego, czy to jest zbudowane z const lub non-const wersją oryginalnego wektora, trzeba zmienić podpis szablonu, aby móc odróżnić const i non-const wersje oryginalnego wektora. Przykładowa implementacja:

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