Как вставить rvalue в карту после вызова на ней функции

Я пытаюсь использовать std::unordered_map<std::string, std::shared_ptr<CObject>> (далее именуемый картой), чтобы иметь возможность использовать карту для вызова одних и тех же функций для разных объектов на основе их имени. Указатели функций на карте были бы одним из способов сделать это, но я подумал, что было бы чище сохранить сами объекты на карте.

Теперь объекты хранятся в shared_ptr, поскольку они имеют зависимости от других применений (функции-члены автоматически вызываются перед вызовами функций и т. д.), а shared_ptr выделяются с помощью функции, которая обертывает make_shared, которая, помимо прочего, также сбрасывает его. as перехватывает и выдает исключения с добавленной релевантной информацией (вероятно, больше не нужно, но это довольно большая кодовая база, и она является частью стандарта кодирования). Эта функция выделяет заданные объекты, используя shared_ptr в качестве ссылки, и возвращает значение void.

Что я хотел сделать, так это создать карту с сопоставлением ключей с объектами и вызвать функцию выделения для всех этих объектов. Но поскольку функция выделения не возвращает OutputIterator или что-то подобное, я не могу использовать std::inserter.

Я не хочу этого делать, используя lvalues, например:

std::map<std::string, std::shared_ptr<CObject>> objectMap;
shared_ptr< CObject> object;
allocate< CObject>( object );
objectMap.insert( make_pair("FirstObject", object ) );

Единственный способ, который я придумал, - это вызвать функцию распределения для несуществующих значений на карте, поскольку map автоматически создаст для меня объекты, если они не существуют. Нравиться:

std::map<std::string, std::shared_ptr<CObject>> objectMap;
allocate< CObject>( objectMap["FirstObject"] );

Любые другие идеи?


person AzP    schedule 27.06.2013    source источник
comment
Но так как функция allocate не возвращает OutputIterator или что-то подобное, я не могу использовать std::inserter. Я не уследил за этим фрагментом - можете ли вы показать, что вы надеялись сделать, но не мог? Допустимо ли перебирать карту дважды, один раз для заполнения ее ключами и еще раз для заполнения указателей? Откуда берутся ключи?   -  person Jonathan Wakely    schedule 27.06.2013
comment
Теперь я добавил больше информации, надеюсь, стало немного легче понять, что я хотел сделать.   -  person AzP    schedule 27.06.2013
comment
Так намного понятнее, спасибо. Ваше решение выглядит нормально, но я добавил еще одно к своему ответу. Мое transform предложение тоже должно быть применимо.   -  person Jonathan Wakely    schedule 27.06.2013


Ответы (1)


Вы можете использовать объект функции, подобный этому, для создания элементов карты:

struct AllocCObject {
  std::pair<const std::string, std::shared_ptr<CObject>>
  operator()(const std::string& key) const
  {
    std::shared_ptr<CObject> p;
    allocate<CObject>(p);
    return { key, p };
  }
};

std::string keys[] =  { "k1", "k2", "k3" };
std::map<std::string, std::shared_ptr<CObject>> map;
std::transform(std::begin(keys), std::end(keys), std::inserter(map), AllocCObject());

Или, если вы предпочитаете использовать лямбду вместо функционального объекта:

auto allocObject = [] (std::string const& s) {
  std::shared_ptr<CObject> p;
  allocate<CObject>(p);
  return std::make_pair(s, p);
} 

В качестве альтернативы, несколько ближе к вашей идее (которая мне нравится):

std::map<std::string, std::shared_ptr<CObject>> map = {
  { "k1", {} }, { "k2", {} }, { "k3", {} }
};
for (auto& e : map)
  allocate<CObject>(e.second);

(Примечание. Я не думаю, что VC++ поддерживает инициализацию карты из такого initializer_list, но это допустимо для C++11.)

person Jonathan Wakely    schedule 27.06.2013
comment
Ключи будут жестко закодированы для каждого значения на карте, и карта, вероятно, будет содержать только 5-6 значений. - person AzP; 27.06.2013
comment
Спасибо, мне нравится решение преобразования, так как оно позволило бы мне создать вектор и определить ключи в одном удобном и лаконичном месте. Можно было бы сделать это с помощью лямбды вместо функционального объекта, верно? Вы правы в вопросе о том, что VC++ не поддерживает initializer_list, так что, к сожалению, это невозможно. - person AzP; 27.06.2013
comment
Да, вы можете заменить функтор лямбдой, такой как [](std::string const& s) { std::shared_ptr<CObject> p; allocate<CObject>(p); return std::make_pair(s, p); } - person Jonathan Wakely; 27.06.2013
comment
Спасибо еще раз! Я добавил лямбда-ответ к исходному ответу! - person AzP; 28.06.2013
comment
Ваше редактирование было отклонено по какой-то причине, я думаю, рецензенты не читали комментарии, поэтому я добавил его сам. - person Jonathan Wakely; 28.06.2013