Подтвердить что ты не робот

С++ Написание константных векторов с указателями на non const

Я пишу класс 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

Итак, поскольку это стандартный С++, я не слишком беспокоюсь, чтобы исправить свой CustomVector, но было бы неплохо узнать, есть ли (не слишком запутанное) решение.

4b9b3361

Ответ 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;
}