【问题标题】:How to check if the iterator is initialized?如何检查迭代器是否已初始化?
【发布时间】:2011-10-20 18:17:28
【问题描述】:

如果我为迭代器使用默认构造函数,如何检查它是否稍后被分配?

对于指针,我可以这样做:

int *p = NULL;
/// some code
if ( NULL == p ) {
  // do stuff
}

如何为迭代器执行上述操作? 有可能吗?

#include <iostream>
#include <list>

int main ()
{
    std::list<int>::iterator it;

  if ( NULL == it ) // this fails
  {
      std::cout<<"do stuff" << std::endl;
  }
}

【问题讨论】:

  • @Wimmel 酷。我想知道我怎么没有得到那个页面。无论如何,它没有回答我的问题
  • 真正的解决方案当然是始终初始化变量。然后你就会知道。 :-)
  • @Bo 我的想法是像未初始化的迭代器一样使用它。我认为这是可能的。像 boost::optional
  • VJo - 它适用于某些迭代器,其中默认构造函数使其成为结束迭代器,但通常不是。而不是容器。

标签: c++ iterator


【解决方案1】:

我设法在当前标准 (c++03) 中找到了这一点。 24.1 p 5 告诉:

就像一个指向数组的常规指针保证有一个 指针值指向数组的最后一个元素,所以对于任何 迭代器类型有一个迭代器值指向最后一个 对应容器的元素。这些值称为 过去的价值。表达式的迭代器 i 的值 *i 被定义为可取消引用。图书馆从不假设 过去的值是可取消引用的。 迭代器也可以有 与任何容器无关的奇异值。 [示例: 在声明未初始化的指针x(与int* x; 一样)之后, 必须始终假定x 具有指针的奇异值。 ] 大多数表达式的结果对于奇异值是未定义的;这 唯一的例外是将非奇异值分配给迭代器 具有奇异值。 在这种情况下,奇异值是 以与任何其他值相同的方式覆盖。可取消引用的值 总是非单数的。

(强调我的)

所以答案是:不,不可能。

【讨论】:

    【解决方案2】:

    我使用了以下解决方案:

    const MyList_t::const_iterator NullIterator(NULL);
    const_iterator MyList_t::MyIterator;
    

    然后可以进行检查:

    if (NullIterator != MyIterator) {}
    

    【讨论】:

      【解决方案3】:

      由于迭代器没有默认值(例如指针为 NULL),在我需要 Object::iterator 的通用默认值的情况下(在创建任何实际对象之前)我创建一个虚拟静态变量并使用其::end() 作为默认值。

      更新:这仅适用于 Release,因为在 DEBUG(或使用 _HAS_ITERATOR_DEBUGGING=1)中,比较运算符会检查两个迭代器是否指向同一个对象/容器。

      例如vector&lt;int&gt; 我会这样做:

      class A
      {
      public :
          A() :  myIterator1(dummyVector.end()), myIterator2(dummyVector.end()) {}
          // needed iterators
          vector<int>::iterator myIterator1;
          vector<int>::iterator myIterator2;
      
          static const vector<int> dummyVector;
      }
      
      #define  IT_NULL A::dummyObject.end()
      
      void maint() {
          A::dummyObject = vector<int>(); // initialize the Null iterator
      
          A a;
          if(a.myIterator1 == IT_NULL) cout << "Iterator not yet initialized";
      }
      

      【讨论】:

        【解决方案4】:

        据我所知,您必须始终初始化迭代器,最简单的方法是使它们等于 'container'.end() 在某些情况下它看起来可以工作,我们在使用 VC6 并停止使用 VC2010 的代码时遇到了一些问题。看看这个用 g++ 编译的例子,它适用于矢量但不适用于地图:

        # Test iterator init, compile with: g++ test-iterator.cpp -o test-iterator
        #include <iostream>
        #include <vector>
        #include <map>
        
        int main()
        {
            std::vector<int> vec;
            std::vector<int>::iterator it;
        
            if (it != vec.end())
            {
                    std::cout << "vector inside!" << std::endl;
            }
            else
            {
                    std::cout << "vector outside!" << std::endl;
            }
        
            std::map<int, int> mp;
            std::map<int, int>::iterator itMap;
        
            if (itMap != mp.end())
            {
                    std::cout << "map inside!" << std::endl;
            }
            else
            {
                    std::cout << "map outside!" << std::endl;
            }
        
            return 0;
        }
        

        【讨论】:

          【解决方案5】:

          我能想到的最好的方法是

          #include <utility>
          #include <map>
          #include <typeinfo>
          #include <string>
          
          
          
          namespace nulliterators {
          
          typedef std::map<std::string, void*> nullcntT;
          nullcntT nullcontainers;
          
          template<class containerT>
          typename containerT::iterator iterator() {
            containerT* newcnt = new containerT();
            std::string cnttypename = typeid(*newcnt).name();
            nullcntT::iterator i = nullcontainers.find(cnttypename);
            if (i==nullcontainers.end()) {
              nullcontainers.insert(make_pair(cnttypename, newcnt));
              return newcnt->end();
             }else{
              delete newcnt;
              return (static_cast<containerT*>(i->second))->end();
            }
          }
          
          }
          template<class containerT>
          typename containerT::iterator nulliterator() { return nulliterators::iterator<containerT>(); }
          
          
          #include <list>
          #include <iostream>
          
          
          int main(){
          
            std::list<int>::iterator nullinitized = nulliterator< std::list<int> >();
            std::list<int> somelist;
            std::list<int>::iterator initialized = somelist.end();
          
            if (nullinitized == nulliterator< std::list<int> >())
              std::cout << "nullinitized == nulliterator< std::list<int> >()\n";  //true
             else
              std::cout << "nullinitized != nulliterator< std::list<int> >()\n";
          
            if (initialized == nulliterator< std::list<int> >())
              std::cout << "initialized == nulliterator< std::list<int> >()\n";
             else
              std::cout << "initialized != nulliterator< std::list<int> >()\n";  //true
          
            return 0;
          }
          

          但这并不是一个完全安全的解决方案(因为它依赖于nullcontainers 中的非常量全局容器)。

          【讨论】:

            【解决方案6】:

            这个问题已经在Stackoverflow 中处理过了。精髓在于默认构造函数将一个迭代器初始化为一个奇异值,对其唯一可添加的操作就是为其分配另一个迭代器值。特别是不可能查询这种未初始化的迭代器的值。因此,将迭代器初始化为特定容器的特定值是一种很好的编程习惯,然后可以对其进行测试。

            【讨论】:

              【解决方案7】:

              在 C++ 中,未初始化的局部变量可以有 any 值,即它只包含垃圾。这意味着,您无法对照某个明确定义的值检查它,以确定该变量是否未初始化。

              不仅如此,如果变量没有初始化并且你这样写:

              if ( NULL == it ) // this fails
              

              然后它调用未定义的行为。

              【讨论】:

              • 对于具有默认构造函数的 UDT 不适用,仅适用于 POD 类型。
              • @DeadMG:具有默认构造函数的 UDT 默认初始化。它们不应该被称为“未初始化”。
              • @DeadMG:你的评论是什么意思?对于 UDT,什么是“不正确的”?
              【解决方案8】:
              if(std::list<int>::iterator() == it)
              

              但我怀疑...有可能,一个有效的迭代器可以通过比较。 最好避免这些情况。如果不可能通过指针存储迭代器。

               std::auto_ptr<std::list<int>::iterator> it;
              

              【讨论】:

                【解决方案9】:

                大多数迭代器没有任何全局特殊值,就像所有指针都可以为 NULL 一样。但是,通常情况下,您将使用特定的容器,并且如果您为每个容器保留一个迭代器,那么您可以使用 end() 作为标记值:

                std::list<int> mylist;
                std::list<int>::iterator it = mylist.end();
                
                /* do stuff */
                
                if (it == mylist.end()) { ... }
                

                不过,我不确定插入/删除是否会使 end() 迭代器无效,因此,如果您打算修改容器,也可以保存原始结尾的副本:

                std::list<int>::iterator end = mylist.end(), it = end;
                
                if (it == end) { ... }
                

                虽然我实际上并不确定比较两个无效的迭代器是否定义明确(如果这两个迭代器确实无效)。

                【讨论】:

                • 特别是对于 std::list,如果您在列表中的任何位置擦除或插入元素,则所有迭代器和对列表中对象的引用仍然有效。当然,您删除的元素除外。对于其他容器,迭代器无效。
                • @VJo:但这是否包括end(),它不是指向任何值的迭代器?
                • 是的,AFAIK 除了向量和双端队列(和字符串),end 将保持有效。
                • 如果容器必须重新分配空间,那么所有迭代器(包括设置为容器的.end()的迭代器!)都会失效。
                【解决方案10】:

                也许你应该总是在创建迭代器之后分配一个预定义的值,比如 NULL。稍后您可以轻松地检查 NULL。 这将使您的代码更具可移植性,因为您将不依赖于未初始化变量在开始时采用的起始值。

                【讨论】:

                  【解决方案11】:

                  你不能。你能做的就是与列表末尾比较

                  it != mylist.end();
                  

                  【讨论】:

                  • 据我所知,没有一般的保证未初始化的迭代器将等于结束迭代器。
                  • 也没有“结束迭代器”。每个对象(实例!)都有自己的结束。 (istreams 除外。)
                  猜你喜欢
                  • 2017-06-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-08-19
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-10-12
                  相关资源
                  最近更新 更多