【问题标题】:C++ Writing const vectors with pointers to non constC++ 用指向非 const 的指针编写 const 向量
【发布时间】:2017-07-07 16:32:17
【问题描述】:

我正在编写一个 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 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,但如果有一个(不太复杂的)解决方案会很高兴。

【问题讨论】:

  • 您需要考虑一个简单的事实,即标准库容器定义了两个不同的迭代器类:iterator 和 const_iterator。这是有充分理由的,而您刚刚发现了这个原因。你的情况完全类似。您需要实现两个不同的引用,一个可变引用和一个const 引用。为了获得额外的荣誉,可变引用应该可以转换为 const 引用,因此它可以传递给以 const 引用作为参数的函数。
  • 犹豫是否将其标记为欺骗,但这是密切相关的stackoverflow.com/questions/44882363/…
  • 使用CustomVector_ref&lt;const int&gt;怎么样?这种方式不需要 const_cast
  • @geza 这会起作用,在这种情况下,如果没有 const_cast,就不可能像任何人所期望的那样写入 const CustomVector 对象。但我认为将 CustomVector_ref 声明为 CustomVector 的引用更为自然,即使它是 const。但我会考虑你的建议。
  • @SamVarshavchik:为什么不回答这个问题?

标签: c++ pointers vector constants


【解决方案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] } 
  ...
};

请注意,将指针存储到原始向量中是危险的。如果原始向量的大小发生变化,您的所有指针值都可能失效。

如果您想根据 const 或 non-const 的原始向量版本来更改 CustomVector_ref 的行为,您需要更改模板的签名以便能够区分 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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    • 2020-02-04
    相关资源
    最近更新 更多