我正在编写一个CustomVector类,使用标准向量在内部存储数据:
template <class T>
class CustomVector {
friend class CustomVector_ref<T>;
public:
...
private:
std::vector<T> _data;
};
然后,为了从CustomVector中提取子向量,我使用一个存储指向每个数据元素的指针的类:
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;
};
现在,为了说明我的问题,考虑构建对CustomVector所有元素的引用的简单构造函数就足够了
template<class T>
CustomVector_ref<T>::CustomVector_ref(CustomVector<T>& cv)
{
for (T& el : cv._data)
_data_ref.push_back(&el);
}
工作正常,但如果我有一个const CustomVector,我还需要定义构造函数:
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));
}
也可以,但是如果CustomVector_ref对象没有声明为const,那么使用非const运算符[]可以将数据写入const 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
可以避免这种行为吗?
我注意到这也发生在标准向量上,例如
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
所以,既然这是标准的C ++,我不会太费心去修复我的CustomVector,但是知道是否有一个(不太复杂的)解决方案会很好。
答案 0 :(得分:1)
我不确定你要做什么。您是否尝试阻止修改原始向量,无论CustomVector_ref
是否使用const
或non-const
版本的向量进行初始化?如果是这样,您只需更改operator[]
的返回类型即可完成此操作,如下所示:
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] }
...
};
请注意,将指针存储到原始矢量中是危险的。如果原始矢量的大小发生变化,则所有指针值都可能无效。
如果您想要更改CustomVector_ref
的行为,具体取决于它是使用原始向量的const
还是non-const
版本构建的,您需要更改其签名模板,以便能够区分原始向量的const
和non-const
版本。示例实现:
#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;
}