【问题标题】:Why must use reference in ranged-based for loops为什么必须在基于范围的 for 循环中使用引用
【发布时间】:2016-04-01 08:33:42
【问题描述】:

我被困在 C++ Primer 中的一段代码中,并且思考了 1 个多小时。代码是

for (auto row : ia)//should use &row here
   for (auto col : row)

对它的解释是

我们这样做是为了避免正常的数组到指针的转换。因为 row 不是引用,当编译器 初始化行,它将转换每个数组元素(就像任何其他数组类型的对象一样) 指向该数组的第一个元素的指针。结果,在这个循环中,行的类型是 诠释*。内部 for 循环是非法的。尽管我们的意图,该循环试图 迭代一个 int*。

我知道这与每次执行 for(auto col:row) 时的迭代有关。我无法理解

我们这样做是为了避免正常的数组到指针的转换"

我们为外循环传入的“ia”是什么形式的?它应该是指向其第一个元素地址的指针而不是“具体”数组吗?那么内循环有什么问题呢?我认为它应该是与外循环相同的机制。我无法理解问答上的帖子。有人请赐教...我的理解有什么问题...也欢迎一个好的链接!提前谢谢了!

ia 的声明是

constexpr size_t rowCnt = 3, colCnt = 4;
int ia[rowCnt][colCnt]; // 12 uninitialized elements
// for each row
  for (size_t i = 0; i != rowCnt; ++i) {
// for each column within the row
    for (size_t j = 0; j != colCnt; ++j) {
// assign the element's positional index as its value
        ia[i][j] = i * colCnt + j;
    }
}

【问题讨论】:

    标签: c++ arrays loops


    【解决方案1】:

    基于范围的 for 循环基于 beginend 函数,它们不适用于 T*,但适用于 T[N]

    实现:

    for ( range_declaration : range_expression ) loop_statement
    

    大致如下:

    {
        auto && __range = range_expression ; 
        for (auto __begin = begin_expr,
            __end = end_expr; 
            __begin != __end; ++__begin) { 
            range_declaration = *__begin; 
            loop_statement 
        } 
    } 
    

    由于在外部 for 循环中数组会衰减为带有 auto row 的指针,因此内部 for 循环将变为非法。

    【讨论】:

      【解决方案2】:

      一般来说,基于范围的 for 循环声明为:

       for ( decl : coll ) {
       statement } 
      

      如果 coll 提供 begin() 和 end() 成员,则等效于以下内容:

          for (auto _pos=coll.begin(), _end=coll.end(); _pos!=_end; ++_pos ) 
         {  
            decl = *_pos; 
             statement 
          } 
      

      或者,如果不匹配,则使用全局 begin() 和 end() 以 coll 作为参数:

      for (auto _pos=begin(coll), _end=end(coll); _pos!=_end; ++_pos )
       { 
          decl = *_pos; 
          statement 
       } 
      

      现在看decl = *_pos;这一行,这里每次编译器初始化 dec 时,它都会将每个数组元素(与任何其他数组类型的对象一样)转换为指向该数组第一个元素的指针。

      在您的情况下,原始类型将是 int*,在该类型上下一个 for 循环无法工作,因此它变得非法。

      【讨论】:

      • 谢谢!我认为你的解释对我来说是最容易理解的。n
      • 本质上是正确的,但等效代码首先评估容器表达式:auto && coll_expr = coll;。此后使用 coll_expr。这使得远程基础可以很好地与例如返回向量的函数。
      • @NirFriedman 感谢您指出这一点!尽管我目前无法理解您在说什么。但是我从他的示例插图中很容易地得到了我想要的东西,所以我接受了他的回答。以后当我读完这本书时,我可能会明白所有这些事情!感谢所有的努力!
      【解决方案3】:

      Range based for 循环适用于具有成员函数beginendarray typeuser defined type

      通过array-to-pointer decay rule,当您将array 传递给采用pointer 的函数时,数组衰减为指针
      但是当您使用**reference** 的模板时,模板类型会被推断为array type

      template<typename T>
      void foo(T&);
      
      int main() {
          int ia[2][2] = {{0,1}, {2,3}};
      
          auto row_val = ia[0]; //int*
          auto& row_ref = ia[0]; //int [2]
      
          foo(row_val); //void foo<int*>(int*&)
          foo(row_ref); //void foo<int [2]>(int (&) [2])      
      }
      

      在这里您可以看到(从它创建的错误!!)为 row_valrow_ref 推断的类型不同

      row_val ==> int*
      row_ref ==> int [2]
      

      您必须使用for(auto&amp; row: ia),因为row 现在是array type 而不是pointer。因此您可以使用内部循环for (auto col : row)

      【讨论】:

        猜你喜欢
        • 2012-10-19
        • 2015-01-15
        • 1970-01-01
        • 1970-01-01
        • 2016-10-31
        • 2016-04-03
        相关资源
        最近更新 更多