Замена C++: замена каждого вхождения v[x] на v.at(x)

В C++ для вектора v v.at(x) ведет себя так же, как v[x], за исключением того, что при доступе к несуществующему элементу выдается ошибка выхода за границы.

Хотелось бы в идеале всегда использовать v.at(x), однако писать не так удобно, как v[x]. Есть ли способ заставить v[x] вести себя как v.at(x), возможно, используя что-то вроде #define?

Если нет, есть ли лучшее решение, чтобы всегда выдавать ошибки за пределами границ?


person Mahathi Vempati    schedule 14.09.2020    source источник
comment
Использование чего-то вроде #define всегда должно быть вашим последним средством. Вам, вероятно, придется написать собственный контейнер для этого, потому что operator[] нельзя перегружать вне класса.   -  person churill    schedule 14.09.2020
comment
[Я] есть ли лучшее решение для ошибок выхода за пределы...? Да, правильная проверка всех индексов. Особенно индексы, которые получены (или получены напрямую) из пользовательского ввода. Вместе с код-ревью и тщательным юнит-тестированием это должно (теоретически) выявить все проблемы.   -  person Some programmer dude    schedule 14.09.2020
comment
Существуют реализации C++, в которых [] ведет себя идентично at, вы можете использовать одну из них.   -  person Caleth    schedule 14.09.2020
comment
Некоторые реализации стандартной библиотеки C++ поддерживают проверку привязки в режиме отладки. См., например, проверка привязки GCC STL или Как заставить компилировать std::vector operator[] с проверкой границ в DEBUG, а не в RELEASE.   -  person Daniel Langr    schedule 14.09.2020
comment
Я часто использую assert. Вы можете свободно использовать его, потому что он не требует дополнительных затрат времени выполнения в сборке выпуска.   -  person Galik    schedule 14.09.2020
comment
Используйте итераторы (и C++11 и более поздние версии) циклы на основе диапазона, сознательно минимизируйте использование векторных индексов и, если вы должны использовать индексы, правильно проверяйте индексы. Вы обнаружите, что существует множество случаев, когда векторных индексов можно избежать, если немного подумать.   -  person Peter    schedule 14.09.2020
comment
@Peter Согласен, но большое преимущество индексов перед итераторами заключается в том, что они остаются действительными после перераспределения, что иногда бывает полезно.   -  person Daniel Langr    schedule 14.09.2020
comment
@DanielLangr - я, конечно, не предлагаю никогда не использовать индексы - как вы говорите, у них есть свое применение. Однако одним из их больших недостатков является то, что ранее проверенный индекс может стать недействительным — и, что еще хуже, не обязательно с проверяемыми симптомами — если размер вектора уменьшен или если какой-то набор его элементов скопирован в другой вектор, а индексы используется на этом другом векторе. Это всего лишь два случая, когда при использовании индексов требуется особая осторожность, чтобы индексы оставались действительными, и программисты слишком часто забывают перепроверять в таких случаях.   -  person Peter    schedule 14.09.2020


Ответы (2)


Вы можете рассмотреть возможность перегрузки метода в новом наследовании следующим образом.

#include <iostream>
#include <vector>

template< class T, class allocator =  std::allocator<T>>
struct Vector : std::vector<T, allocator>{
    using std::vector<T, allocator>::vector;

    const T& operator[](size_t i)const{
        return this -> at(i);
    }

    T& operator[](size_t i){
        return this -> at(i);
    }
};

template< class T>
Vector(size_t, T ) -> Vector<T>;//if u want to use c++17 deduction guides

int main()
{
    std::vector<int> vec1(4,1);
    std::cout << vec1[4];

    Vector vec2(4,1);
    std::cout << vec2[4];

}

person asmmo    schedule 14.09.2020

Вы можете использовать прокси-объект с оператором [], который вызывает метод at(). Конечно, это не совсем то, что вам нужно, но синтаксис похож: вместо v[x] нужно писать at(v)[x].

Доказательство концепции:

#include <iostream>
#include <vector>


template<class T>
class AtProxy
{
  public:
    AtProxy(const AtProxy<T>& proxy) : m_obj{proxy.m_obj} {}
    AtProxy(T& obj) : m_obj{obj} {}
    typename T::reference operator [](size_t index)
    {
      return m_obj.at(index);
    }
  private:
    T& m_obj;
};


template<class T>
AtProxy<T> at(T& v)
{
  return AtProxy<T>(v);
}


int main()
{
  try
  {
    std::vector<int> vec = {1,2,3,4,5};

    for(size_t i = 0; i < 5; i++)   
    {
      std::cout << i << std::endl;
      at(vec)[i] = at(vec)[i + 1] + 1;
    }   
  }
  catch(std::exception& e)
  {
    std::cout << "exception: " << e.what() << std::endl;
  }
   
  std::cout << "ok" << std::endl;
  return 0;
}
person B0FEE664    schedule 14.09.2020