Boost.Bind для доступа к элементам std :: map в std :: for_each - PullRequest
8 голосов
/ 22 февраля 2010

У меня есть карта, в которой хранится простая структура с ключом. Структура имеет две функции-члена, одна из которых является константой, а другая - нет. Мне удалось без проблем вызвать const-функцию с помощью std :: for_each, но у меня возникли некоторые проблемы с вызовом неконстантной функции.

struct MyStruct {
  void someConstFunction() const;
  void someFunction();
};

typedef std::map<int, MyStruct> MyMap;
MyMap theMap;

//call the const member function
std::for_each(theMap.begin(), theMap.end(),
   boost::bind(&MyStruct::someConstFunction, boost::bind(&MyMap::value_type::second, _1)));

//call the non-const member function
std::for_each(theMap.begin(), theMap.end(),
   boost::bind(&MyStruct::someFunction, boost::bind(&MyMap::value_type::second, _1)));

Вызов функции-члена const работает нормально, но кажется, что boost внутренне ожидает где-то const MyStruct, и поэтому завершается ошибкой со следующей ошибкой компиляции в MSVC7.1.

boost \ bind \ mem_fn_template.hpp (151): ошибка C2440: «аргумент»: невозможно преобразовать из «const MyStruct * __ w64» в «MyStruct * const»

Буду признателен за помощь в правильной настройке параметров шаблона, поэтому bind правильно распознает параметры и разрешит мне вызывать неконстантную функцию.

спасибо, Карл

Ответы [ 4 ]

8 голосов
/ 22 февраля 2010

IIRC, Boost.Bind использует boost::mem_fn для привязки к возможностям членов. Теперь, если вы посмотрите на mem_fun (прокрутите вниз до части // data member support), вы увидите, что он typedef определяет свой result_type как const &, в то время как все еще имеет перегрузки оператора вызова функции, поддерживающего извлечение неконстантного члена из неконстантного аргумента.

Таким образом, похоже, проблема в том, что это сбивает с толку механизм вывода типа Boost.Bind. Таким образом, решение было бы явно указать Bind, что результат не является константой:

//call the non-const member function
std::for_each(theMap.begin(), theMap.end(),
   boost::bind(&MyStruct::someFunction, 
       boost::bind<MyStruct&>(&MyMap::value_type::second, _1)
   )
);
7 голосов
/ 22 февраля 2010

Если вам часто приходится этим заниматься, я рекомендую использовать библиотеку Boost.RangeEx:

#include <boost/range/algorithm/for_each.hpp>
#include <boost/range/adaptor/map.hpp>
#include <boost/mem_fn.hpp>
#include <map>

struct MyStruct {
  void someConstFunction() const;
  void someFunction();
};

typedef std::map<int, MyStruct> MyMap;
MyMap theMap;

int main()
{
    //call the const member function
    boost::for_each(theMap | boost::adaptors::map_values,
                    boost::mem_fn(&MyStruct::someConstFunction));

    //call the non-const member function
    boost::for_each(theMap | boost::adaptors::map_values,
                    boost::mem_fn(&MyStruct::someFunction));
}

Он был принят в Boost, но пока не поставляется с официальным дистрибутивом. Пока это не произойдет, вы можете загрузить его из Boost Vault (ссылка на скачивание zip-файла).

4 голосов
/ 22 февраля 2010

Если вы уже зависите от Boost, возможно, вы захотите проверить Boost Foreach

BOOST_FOREACH(MyMap::value_type const& val, MyMap)
{
  val.second.someConstFunction();
}

Много читабельного, хотя я не знаю о проблемах с производительностью.

Также обратите внимание, что вы не можете использовать шаблонизированный типизированный внутри макроса без "экранирования" символа ,:

  • либо typedef до
  • или с помощью второй пары скобок вокруг типа
0 голосов
/ 22 февраля 2010

Одна проблема, которую я обнаружил: вторая привязка вызывается для не функционального члена. второй - элемент данных, а не метод std :: pair

...