Замяна на 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
[I]има ли по-добро решение за грешки извън границите...? Да, правилно валидиране на всички индекси. Особено индекси, които се извличат (или идват директно) от потребителско въвеждане. Заедно с преглед на кода и задълбочено модулно тестване трябва (теоретично) да улови всички проблеми.   -  person Some programmer dude    schedule 14.09.2020
comment
Има реализации на C++, които избират [] да се държи идентично с at, можете да използвате едно от тях.   -  person Caleth    schedule 14.09.2020
comment
Някои реализации на стандартната библиотека на C++ поддържат обвързана проверка в режим на отстраняване на грешки. Вижте например GCC STL обвързана проверка или Как да накарате компилацията operator[] на std::vector да извършва проверка на границите в 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