Использование вложенных операций [] для std :: vector - PullRequest
1 голос
/ 27 мая 2020

Я новичок в C ++, и я пытался найти ответ на этот вопрос и провести тесты, но много раз у меня возникали проблемы с выяснением причин определенного c поведения. Мой вопрос касается использования вложенных операторов [ ] для доступа или изменения элементов в al oop - пример:

//Declare

std::vector<int> a1 {10,20,30,40} ;
std::vector<int> a2 {2,3} ;
int S2 = a2.size() ; 

//Loop
for(int i = 0 ; i < S2 ; i++){
         a1[a2[i]] = a1[a2[i]] + 5000 ;
}

Считается ли это нормальным? Я спрашиваю не только с точки зрения общей практики, но и с точки зрения эффективности и любого другого потенциального фактора, который мне нужно учитывать. Должен ли я сначала сохранить a[i] во временной переменной внутри l oop, а затем использовать его для изменения моего элемента в векторе a2?

Я знаю, что это, вероятно, не лучшая структура и Я должен использовать какую-то другую структуру данных для такого рода вещей, но я просто хочу понять, нормально ли это или это может вызвать неопределенное поведение.

Ответы [ 4 ]

6 голосов
/ 27 мая 2020

Я разработчик программного обеспечения для расчета методом конечных элементов.

Мы используем эту технику для доступа к значениям внутри элемента. Это помогает нам экономить много памяти

НО: имейте в виду, что это портит расположение вашего кеша. Не используйте его в тяжелых циклах, если вы можете этого избежать.

Если вам нужны проверки диапазона, а производительность не важна, вы можете рассмотреть возможность использования оператора at в std::vector

 for(const auto & index :a2) {
      a1.at(index) += 5000;
 }

Функция at автоматически проверяет, находится ли n в пределах допустимых элементов в векторе, генерируя исключение out_of_range, если это не так (т. Е. Если n больше или равно его размеру) . Это контрастирует с оператором-членом [], который не проверяет границы.

Более того, рассмотрите возможность использования диапазона на основе l oop

 //Loop
 for(const auto & index :a2) {
      a1[index] += 5000;
 }
5 голосов
/ 27 мая 2020

Это совершенно правильно.

Но на самом деле вы просто хотите перебрать элементы стандартного контейнера. C ++ позволяет использовать диапазон на основе оператора для этого варианта использования:

for (index: a2) {
    a1[index] += 5000;
}

Я считаю его более читаемым, даже если это в основном вопрос вкуса ...

Отказ от ответственности: этот код не контролирует достоверность элементов a2 как индекса a1.

2 голосов
/ 27 мая 2020

Мне кажется, это нормально. Нет необходимости создавать явную копию a2[i].

Единственная проблема, которую я вижу с чем-то вроде этого, заключается в том, что аргумент внутри [] должен иметь тип std::size_t вместо int. Эти целочисленные типы охватывают различные диапазоны значений, и хотя std::size_t является целым числом без знака, int является целым числом со знаком. Остерегайтесь использования отрицательных индексов или индексов после последнего элемента, скорее всего, это приведет к неопределенному поведению из-за доступа за пределы. Но если вы можете гарантировать, что значения в a2 всегда являются действительными индексами для a1, тогда эти int значения будут неявно преобразованы в std::size_t, и все будет работать правильно (что, похоже, имеет место в примере кода в вашем вопросе).

Я также предлагаю преобразовать переменную l oop i в std::size_t (и использовать ++i вместо i++, если вы хотите быть идеальным :).

В современном C ++ вы также можете использовать диапазон на основе, поэтому вам вообще не нужно использовать явную индексную переменную для доступа к a2 значениям:

for (auto indexFromA2 : a2)
    a1[indexFromA2] += 5000;

Это меньше подвержен ошибкам, потому что вам нужно писать меньше c logi для управления доступом к элементам (и не нужно указывать типы).

1 голос
/ 27 мая 2020

Я бы каким-то образом удостоверился, что элементы в a1, определенные в a2, действительно существуют, прежде чем пытаться получить к ним доступ, иначе вы выйдете за пределы.

Но в отношении вложенного [] это нормально, и нет необходимости создавать еще одну копию a2 для доступа к a1. Компилятор просто разворачивает ваше выражение изнутри.

Вы все равно можете немного упростить свой код

 //Declare
 std::vector<int> a1 {10,20,30,40} ;
 std::vector<int> a2 {2,3} ;

 //Loop
 for(int i = 0 ; i < a2.size() ; i++){
          if(a1.size()-1 < a2[i]){break;}
          a1[a2[i]] += 5000 ;
 }
...