2017-07-07 60 views
10

我写一个CustomVector类,使用标准载体内部存储数据载体:C++编写常量与指针非const的

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

现在,来说明我的问题就足够了考虑简单costructor建立一个参考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,但它会很高兴知道如果有(不太复杂)的解决方案。

+18

您需要思考一个简单的事实:标准库容器定义了两个不同的迭代器类:'iterator'和'const_iterator'。这有一个很好的理由,你刚刚发现了这个原因。你的情况完全是类似的。你需要实现两个不同的引用,一个可变参数和一个'const'引用。对于额外的功劳,可变引用应该可转换为'const'引用,所以它可以传递给以'const'引用为参数的函数。 –

+0

犹豫要将它标记为伪装,但这是密切关注https://stackoverflow.com/questions/44882363/does-const-iterator-really-need-to-be-a-different-class-than-iterator – user463035818

+1

什么关于使用'CustomVector_ref '?没有const_cast需要这种方式 – geza

回答

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取决于它与一个constnon-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; 
}