为什么不基于范围的循环修改容器元素?

时间:2019-07-03 14:38:52

标签: c++ c++11 auto

我最近发现修改自动迭代向量中的数据不会为我产生正确的结果。例如,当我尝试对vector的vector元素进行排序时,某些元素没有进行排序,但是代码运行成功

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 个答案:

答案 0 :(得分:10)

您的for循环复制v,然后对其进行排序。原稿未触碰。您想要的是for (auto &v : arr)

答案 1 :(得分:2)

  

我最近才知道,当修改基于for循环的范围内的数据时,结果是不确定的。

您完全错了。如果您修改容器本身进行迭代(例如向其添加或删除元素),而不是修改容器中包含的数据,则这是未定义行为:

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

vs:

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

您的问题的其余部分是毫无意义的,因为它基于此错误的假设。您没有观察到向量变化内的数据的原因有所不同,并且已经得到解决。但是,即使您修复了代码并使其修改了容器数据,也还是可以的。

需要注意的一点:即使声明修改您遍历的容器是UB,也太笼统了。例如,您可以在std::list中进行迭代时删除元素,但仍避免使用UB。但是当然不应该编写这样的代码,因为这样很容易出错,在这种情况下应该在迭代器范围内进行迭代。

答案 2 :(得分:2)

这里没有什么是未定义的,没有理由出现语法错误。

您的循环按值迭代外部向量 ,然后对内部向量的本地副本进行排序。这没有意义,但这不是顽皮的。

  

我最近才知道,当修改基于for循环的范围内的数据时,结果是不确定的。

您不能在结构上修改要迭代的内容,因为这样做会破坏迭代。

但这不是您所做的。即使您编写了auto& v : arr,从而修改了内部向量的值,您仍然没有执行任何操作来中断外部向量arr的迭代。

不过,请不要在循环内写入arr.clear()

  

为什么修改自动迭代数据不是语法错误?

即使您的程序具有未定义的行为,也绝不会出现语法错误,甚至通常不会出现运行时错误。

但是,如果有一条笼统的规则指出在基于范围的for循环内无法执行任何变异操作,请放心,该语言的语义可能会导致编译时错误,阻止您执行此操作(例如无法构建直接修改const int的程序)。

答案 3 :(得分:2)

  

为什么修改自动迭代数据不是语法错误?

因为修改后的变量在语法上结构合理,无论变量是否为引用。示例:

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循环的范围内修改变量不会产生不确定的结果。

有些操作可能不会在要迭代的范围上执行,尤其是那些使迭代器/对迭代范围内的元素的引用无效的操作。您可能对此感到困惑。

您显示的程序具有明确的行为。但是,您可能打算对向量元素进行排序,但尚未成功完成。为了做到这一点,您必须引用到向量的元素,而不是进行复制。这可以通过使用引用来实现:

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

当我以sort(arr [0] .begin(),arr [0] .end())进行排序时,结果是正确的。

下标运算符返回引用