Подтвердить что ты не робот

Как проверить, инициализирован ли итератор?

Если я использую конструктор по умолчанию для итератора, как проверить, назначен ли он позже?

Для указателей я мог бы сделать это:

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;
  }
}
4b9b3361

Ответ 1

Мне удалось найти это в текущем стандарте (С++ 03). 24.1 п 5 рассказывает:

Точно так же, как обычный указатель на массив гарантирует, что существует значение указателя, указывающее на последний элемент массива, так и для любого типа итератора существует значение итератора, которое указывает на последний элемент соответствующего контейнера. Эти значения называются прошлыми значениями. Значения итератора i, для которых определено выражение *i, называются разыменованными. Библиотека никогда не предполагает, что последние значения являются разыменованными. Итераторы также могут иметь особые значения, которые не связаны ни с одним контейнером. [Пример: после объявления неинициализированного указателя x (как в случае int* x;) всегда следует предполагать, что x имеет единственное значение указателя. ] Результаты большинства выражений не определены для единичных значений; единственное исключение - это присвоение сингулярного значения non- итератору, который содержит сингулярное значение. В этом случае единственное значение перезаписывается так же, как и любое другое значение. Разыменовываемые значения всегда non- единственного числа.

(Акцент мой)

Таким образом, ответ: нет, это невозможно.

Ответ 2

Большинство итераторов не имеют каких-либо глобальных специальных значений таким же образом, что все указатели могут быть 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) { ... }

Хотя я снова не уверен, правильно ли он сопоставлен двум недействительным итераторам (в случае, если они действительно становятся недействительными).

Ответ 3

Вы не можете. Вы можете сделать, это сравнить с концом конца

it != mylist.end();

Ответ 4

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

Не только это, если переменная не инициализирована, и вы пишете это:

if ( NULL == it ) // this fails

то он вызывает поведение undefined.

Ответ 5

Этот вопрос уже рассмотрен в fooobar.com/questions/109468/.... Квинтэссенция заключается в том, что конструктор по умолчанию инициализирует итератор сингулярным значением, и единственной допустимой операцией на нем является присвоение ему другого значения итератора. В частности, невозможно запросить значение такого униализированного итератора. Поэтому хорошей практикой программирования является инициализация итератора до определенного значения конкретного контейнера, который затем может быть проверен.

Ответ 6

Поскольку для итераторов нет значения по умолчанию (например, NULL для указателей), в ситуации, когда мне нужно общее значение по умолчанию для Object::iterator (до того, как был создан какой-либо фактический объект), я создаю фиктивную статическую переменную и используйте ::end() по умолчанию.

Обновление: это работает только для Release, потому что в операторах сравнения DEBUG (или с _HAS_ITERATOR_DEBUGGING=1) проверяют, указывают ли оба итератора на один и тот же объект/контейнер.

Например, для vector<int> я бы сделал:

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";
}

Ответ 7

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

Ответ 8

if(std::list<int>::iterator() == it)

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

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

Ответ 9

Лучший способ сделать это, о котором я могу думать, - это что-то вроде

#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;
}

но это не совсем безопасное решение (потому что оно опирается на не-const глобальные контейнеры в nullcontainers).

Ответ 10

Насколько я знаю, вы всегда должны инициализировать свои итераторы, и самый простой способ - сделать их равными "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;
}

Ответ 11

Я использовал следующее решение:

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

Тогда возможна проверка:

if (NullIterator != MyIterator) {}