Почему не основано на диапазоне для элементов контейнера, изменяющих цикл? - PullRequest
0 голосов
/ 03 июля 2019

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

vector<vector<int> > arr;
arr.push_back({38, 27});
for(auto v : arr)
{
    sort(v.begin(), v.end());
}

Вывод вышеупомянутого кода после сортировки все еще 38, 27 после сортировки. Тогда как когда я сортирую как sort (arr [0] .begin (), arr [0] .end ()), результат правильный. Я скомпилировал с помощью gcc.

Ответы [ 4 ]

10 голосов
/ 03 июля 2019

Ваш для копий цикла v, затем сортирует его.Оригинал не тронут.То, что вы хотите, это for (auto &v : arr).

2 голосов
/ 03 июля 2019

Почему модификация автоматически повторяющихся данных не является синтаксической ошибкой?

Поскольку он синтаксически правильно сформирован для изменения переменных, независимо от того, является ли переменная ссылкой или нет.Пример:

int j = 0
int& i = j; // i refers to j
i = 42; // OK; j is modified


int j = 0
int i = j; // i is a copy of j
i = 42; // OK even though i is not reference
        // j is not modified; only i is

int j = 0
auto i = j; // i is a copy of j
i = 42; // OK; same as above except using auto deduced type
        // j is not modified

std::vector<int> int_vec(10);
for(int i : int_vec)
    i = 42; // OK; same as above except within a loop
            // the vector is not modified

for(auto i : int_vec) // i is a copy
    i = 42; // OK; same as above except using both auto and a loop
            // the vector is not modified

for(auto& i : int_vec) // i is a reference
    i = 42; // OK; elements of the vector are modified

Недавно я узнал, что при изменении данных внутри диапазона, основанного на цикле, результат не определен.

Вы столкнулись с неверными знаниями.Изменение переменных внутри диапазона на основе цикла не приводит к неопределенному результату.

Существуют некоторые операции, которые могут не выполняться с диапазоном, который повторяется, в частности те, которые делают недействительными итераторы / ссылки на элементы повторяемого элемента.спектр.Возможно, вас это смутило.

Программа, которую вы показываете, имеет четко определенное поведение.Однако вы, возможно, намеревались отсортировать элементы вектора, что вы не сделали успешно.Чтобы сделать это, вы должны ссылаться на элементы вектора, а не делать копию.Это достигается с помощью ссылки:

for( auto &v : arr )
          ^ this makes the variable a reference

В то время как при сортировке по сортировке (arr [0] .begin (), arr [0] .end ()) результат верный.

Оператор нижнего индекса возвращает ссылку .

2 голосов
/ 03 июля 2019

Ничто здесь не определено, и нет никаких причин для синтаксической ошибки.

Ваш цикл повторяет внешний вектор по значению , затем сортирует локальную копию внутреннего вектора. Это бессмысленно, но не непослушно.

Недавно я узнал, что при изменении данных внутри диапазона, основанного на цикле, результат не определен.

Вы не можете структурно изменить то, что вы повторяете, правда, так как это нарушает итерацию.

Но это не то, что вы сделали. Даже если вы написали auto& v : arr и таким образом изменили значения внутренних векторов, вы все равно не выполняете никаких операций, которые нарушают итерацию внешнего вектора arr.

Не пишите arr.clear() внутри цикла, хотя!

Почему модификация автоматически повторяющихся данных не является синтаксической ошибкой?

Даже если ваша программа имела неопределенное поведение, это никогда не является синтаксической ошибкой, а часто даже ошибкой времени выполнения.

Но если бы было общим правилом, утверждающим, что внутри цикла for, основанного на диапазоне, невозможно выполнить какие-либо операции изменения, будьте уверены, что семантика языка, вероятно, потребует остановки ошибки времени компиляции вам от этого (например, как вы не можете создать программу, которая изменяет const int напрямую).

2 голосов
/ 03 июля 2019

Недавно я узнал, что при изменении данных внутри диапазона, основанного на цикле, результат не определен.

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

 std::vector<int> v( 10 );
 for( auto i : v ) v.push_back( 10 ); // UB as v is modified, new element inserted to it

против:

 std::vector<int> v( 10 );
 for( auto &i : v ) i = 123; // totally fine you modify elements inside vector

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

Замечание по педантичности: утверждение даже о том, что модифицировать контейнер, перебираемый вами, является UB, является слишком общим.Вы можете, например, удалить элементы в std::list во время итерации по нему и при этом избегать UB.Но, конечно, такой код вообще не должен быть написан, так как он будет весьма подвержен ошибкам, и в этом случае следует выполнять итерации по диапазону итераторов.

...