首页 > 解决方案 > 在基于 C++ 范围的 for 循环中,begin() 可以返回对不可复制迭代器的引用吗?

问题描述

更新:

感谢所有提交答案的人。

简而言之,答案是返回的“迭代器”begin()必须end()是可复制的。

Artyer 提出了一个很好的解决方法:创建一个包含对不可复制对象的引用(或指针)的迭代器类。下面是示例代码:

struct  Element  {};

struct  Container  {

  Element  element;

  struct  Iterator  {
    Container *  c;
    Iterator  ( Container * c )  :  c(c)  {}
    bool  operator !=  ( const Iterator & end )  const  { return  c != end.c; }
    void  operator ++  ()  {  c  =  nullptr;  }
    const Element &  operator *  ()  const  {  return  c->element;  }
  };

  Iterator  begin  ()  {  return  Iterator ( this    );  }
  Iterator  end    ()  {  return  Iterator ( nullptr );  }

};

#include  <stdio.h>
int  main  ()  {
  Container  c;
  printf ( "main  %p\n", & c .element );
  for  (  const Element & e  :  c  )  {  printf ( "loop  %p\n", & e );  }
  return  0;
}

原始问题:

以下 C++ 代码将无法编译(至少g++在 Ubuntu 20.04 上的 9.3.0 版本中无法编译)。

错误信息是:
use of deleted function 'Iterator::Iterator(const Iterator&)'

基于该错误,我是否正确得出结论“迭代器”返回begin()并且end()必须是可复制的?或者有什么方法可以使用通过引用返回的不可复制的迭代器?

struct  Iterator  {

  Iterator  ()  {}
  //  I want to prevent the copying of Iterators, so...
  Iterator  ( const Iterator & other )  =  delete;

  bool        operator !=  ( const Iterator & other )  {  return  false;   }
  Iterator &  operator ++  ()  {  return  * this;  }
  Iterator &  operator *   ()  {  return  * this;  }

};

struct  Container  {

  Iterator  iterator;

  Iterator &  begin()  {  return  iterator;  }
  Iterator &  end()    {  return  iterator;  }

};

int  main  ()  {
  Container  container;
  for  (  const Iterator & iterator  :  container  )  {}
  //  The above for loop causes the following compile time error:               
  //  error: use of deleted function 'Iterator::Iterator(const Iterator&)'      
  return  0;
}

标签: c++iteratorcopy-constructorrange-based-loop

解决方案


是的,迭代器必须是可复制的。这是因为范围迭代在逻辑上等价于以下代码

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

        range_declaration = *__begin;
        loop_statement

    }

这是 C++11 版本。C++17 及更高版本略有不同,但根本原因是相同的:__beginand __endareauto和 notauto &或类似的东西。它们是非引用类型。“begin_expr”和“end_expr”,在很多话中,是最终调用您的自定义and的beginand表达式。endbegin()end()

即使您的begin()end()返回引用,它们也会被分配给非引用类型,因此必须是可复制的。

请注意,即使不是这种情况,显示的实现也不是很有用,因为两个引用总是相同的对象,所以 begin 和 end 表达式最终将是相同的对象,并且总是比较相等 (希望)。


推荐阅读