I am writing a CustomVector class, internally storing data using a standard vector:
template <class T>
class CustomVector {
friend class CustomVector_ref<T>;
public:
...
private:
std::vector<T> _data;
};
Then, in order to extract subvectors from CustomVector, I use a class storing pointers to each element of data:
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;
};
Now, to illustrate my problem it is sufficient to consider the simple costructor building a reference to all the elements of CustomVector
template<class T>
CustomVector_ref<T>::CustomVector_ref(CustomVector<T>& cv)
{
for (T& el : cv._data)
_data_ref.push_back(&el);
}
That works fine, but if I have a const CustomVector, I need also to define the constructor:
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));
}
That works too, but if the CustomVector_ref object is not declared as const, then with the non-const operator [] it is possible to write data into the const CustomVector object.
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
It is possible to avoid this behavior?
I have noticed that this happens also with standard vectors, for example
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
So, since this is standard C++, I do not bother too much to fix my CustomVector, but it would be nice to know if there is a (not too convoluted) solution.
I'm not exactly sure what you are trying to do. Are you trying to prevent modification of the original vector regardless of whether CustomVector_ref
was initialized with a const
or non-const
version of the vector? If so, you can do this by simply changing the return type of operator[]
, as follows:
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] }
...
};
Note that storing pointers into the original vector is dangerous. If the original vector's size changes, all your pointer values may be invalidated.
If you want to change the behavior of CustomVector_ref
depending on whether it's constructed with a const
or non-const
version of the original vector, you'll need to change the signature of the template in order to be able to differentiate between const
and non-const
versions of the original vector. An example implementation:
#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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With