Почему нельзя использовать std::bind и boost::bind взаимозаменяемо в этом руководстве по Boost.Asio

Я пробовал разные руководства в документации Boost.Asio и пытался заменить компоненты boost на C++11. Однако я получил ошибку при использовании std::bind в Timer.5 - Синхронизация обработчиков в многопоточных программах. Вот предлагаемый код:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

class printer { /* Not relevent here */ };

int main()
{
  boost::asio::io_service io;
  printer p(io);
  boost::thread t(boost::bind(&boost::asio::io_service::run, &io));
  io.run();
  t.join();

  return 0;
}

Я попытался заменить boost::thread на std::thread и boost::bind на std::bind. Вот мой код:

#include <functional>
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

class printer { /* Not relevent here */ };

int main() {
    boost::asio::io_service io;
    printer p(io);
    std::thread t(std::bind(&boost::asio::io_service::run, &io));
    io.run();
    t.join();
}

При компиляции с GCC 4.7 я получил эту ошибку времени компиляции:

g++ -std=c++0x main.cpp -lboost_system -lboost_date_time -lpthread
main.cpp: In function ‘int main()’:
main.cpp:52:60: erreur: no matching function for call to ‘bind(<unresolved overloaded function type>, boost::asio::io_service*)’
main.cpp:52:60: note: candidates are:
/usr/include/c++/4.6/functional:1444:5: note: template<class _Functor, class ... _ArgTypes> typename std::_Bind_helper::type std::bind(_Functor&&, _ArgTypes&& ...)
/usr/include/c++/4.6/functional:1471:5: note: template<class _Result, class _Functor, class ... _ArgTypes> typename std::_Bindres_helper::type std::bind(_Functor&&, _ArgTypes&& ...)

Откуда эта ошибка, принимая во внимание, что я не использовал никаких boost::asio::placeholders (как объясняется в этом вопросе о переполнении стека -boostasio">Должен ли std::bind быть совместим с boost::asio?)?


person authchir    schedule 28.01.2012    source источник
comment
Возможный дубликат: см. stackoverflow.com/questions/ 8924149/).   -  person mark    schedule 29.01.2012
comment
Поскольку вы уже используете C++11: лямбда-выражения могут быть для вас альтернативой std::bind, например, std::thread t([&io]() { io.run(); });. Это позволяет полностью избежать разрешения перегрузки.   -  person mavam    schedule 14.02.2012


Ответы (4)


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

Чтобы обойти эту проблему, вы можете явно указать тип первого аргумента для std::bind() (он также должен работать с boost::bind()), например. нравится:

std::bind(static_cast<size_t (boost::asio::io_service::*)()>(&boost::asio::io_service::run), &io);

Я не проверял, предъявляет ли стандарт какие-либо требования, но если он действительно не выполняет никаких требований, я бы рассмотрел реализацию, которая не переходит к героизму, чтобы вывести тип аргумента как более качественный, хотя он делает меньше работы: он требует, чтобы пользователь написал код, который без изменений может быть скомпилирован другим компилятором.

person Dietmar Kühl    schedule 28.01.2012

Небольшое замечание: в C++11 и выше вы можете просто использовать лямбда-выражения, чтобы избежать всего лишнего и значительно упростить все это. Старый:

boost::thread t(boost::bind(&boost::asio::io_service::run, &io));

или std::version:

std::thread t(boost::bind(static_cast<size_t (boost::asio::io_service::*)()>(&boost::asio::io_service::run), &io_service));

стать просто:

std::thread t([&io_service](){io_service.run();});
person Riot    schedule 12.11.2014
comment
просто так лучше! - person UmNyobe; 17.06.2016
comment
Часто вы хотели бы поместить этот поток в вектор. но написанный как лямбда, вектор не перемещается, поэтому вам нужно будет использовать threadPool.emplace_back(std::move(t)) - person kroiz; 16.09.2019

Это была огромная PITA, которую нужно было понять, поэтому спасибо Дитмару за подсказку. для тех, кто использует boost::bind, ваше решение выглядит так:

// create the io_service    

boost::asio::io_service io_service;

 // assign some work to asio before starting
 {
    io_service.post(&some_work);   // just for example
    ....
 }

boost::thread t(boost::bind(static_cast<size_t (boost::asio::io_service::*)()>(&boost::asio::io_service::run), &io_service));

// work should be executed in a new thread

t.join()

return;
person William Symionow    schedule 02.01.2013

Поскольку вы уже используете C++11: lambdas может быть для вас альтернативой std::bind, например, std::thread t(&io { io.run(); });. Это позволяет полностью избежать разрешения перегрузки.

Чтобы получить правильный результат, решение (в автономном режиме asio или boost:: asio):

asio::io_service io;
auto ptrToIoService = &io;
printer p(io);

//asio::thread t(std::bind(&asio::io_service::run, &io));
//asio::thread t([&io]() {io.run();});
asio::thread t([ptrToIoService] () { ptrToIoService->run();});

See "Effective Modern C++" under "Item 31 Avoid default capture modes."

person Val    schedule 26.06.2016