Создает ли std :: map <K, V> :: iterator std :: map <K, V>?

У меня есть этот код, который работает на GCC:

#include <map>

class Foo;
class Bar;

typedef std::map<Foo,Bar> MyMap;

MyMap::iterator i;

class Foo
{
    MyMap::iterator some_data;
};

Код, разработанный в настоящее время (который неприятно цикличен, да, я застрял с ним) требует, чтобы map<Foo,Bar>::iterator был доступен для Foo и Bar.

Это работает, потому что реализации библиотеки GCC не требуется создавать экземпляр типа ключа карты для создания экземпляра итератора.

Это гарантировано? Стандарт, кажется, несколько непривычен, когда дело доходит до определения типа итератора карты. Насколько переносим этот код?

Ответ 1

Это приводит к неопределенному поведению.

В объявлении MyMap::iterator i; MyMap должен быть полным типом, поэтому он создается неявно. Тем не менее, Foo и Bar не завершены на этом этапе создания, поэтому поведение не определено в соответствии с [res.on.functions]/2:

В частности, эффекты не определены в следующих случаях:

  • ...
  • если неполный тип ([basic.types]) используется в качестве аргумента шаблона при создании экземпляра компонента шаблона или оценке концепции, если это специально не разрешено для этого компонента.

Ответ 2

Вы можете обойти весь вопрос, учитывая тот факт, что std::map является контейнером на основе узлов, поэтому его узлы, содержащие элементы, имеют стабильные адреса. Т.е. вы можете использовать простой указатель вместо итератора (если, конечно, вам не нужен итератор для передачи в функции-члены std::map):

class Foo
{
    std::pair<Foo const, Bar>* some_data;
};

Обратите внимание, что здесь нужны только объявления для Foo, Bar и std::pair<>, чтобы определить элемент some_data.

Если вы используете boost::multi_index (который во многих отношениях превосходит ассоциативные контейнеры std), то он имеет чрезвычайно полезные функции to_iterator которые принимают ссылку на элемент и возвращают итератор.