为什么boost :: asio :: io_service不能用std :: bind编译?

ecl*_*pse 11 c++ boost boost-asio c++11

我正在尝试编译简单的测试程序std::thread,std::bindboost::asio使用g++4.9.1(-std=c++11).

但是,在创建新线程时,它在我使用时不会编译std::bind.另一方面,当我切换到boost::bind一切都很好.

这是代码:

#include <iostream>
#include <memory>
#include <thread>
#include <functional>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

int main(int argc, char* argv[])
{
    boost::asio::io_service ioService;
    std::unique_ptr<std::thread> t;

    t.reset(new std::thread(std::bind(&boost::asio::io_service::run, &ioService)));
    //t.reset(new std::thread(boost::bind(&boost::asio::io_service::run, &ioService)));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是错误:

test.cpp: In function ‘int main(int, char**)’:
test.cpp:12:80: error: no matching function for call to ‘bind(<unresolved overloaded function type>, boost::asio::io_service*)’
    t.reset(new std::thread(std::bind(&boost::asio::io_service::run, &ioService)));
                                                                                            ^
test.cpp:12:80: note: candidates are:
In file included from /usr/include/c++/4.9/memory:79:0,
             from test.cpp:2:
/usr/include/c++/4.9/functional:1623:5: note: template<class _Func, class ... _BoundArgs> typename std::_Bind_helper<std::__or_<std::is_integral<typename std::decay<_Tp>::type>, std::is_enum<typename std::decay<_Tp>::type> >::value, _Func, _BoundArgs ...>::type std::bind(_Func&&, _BoundArgs&& ...)
 bind(_Func&& __f, _BoundArgs&&... __args)
 ^
/usr/include/c++/4.9/functional:1623:5: note:   template argument deduction/substitution failed:
test.cpp:12:80: note:   couldn't deduce template parameter ‘_Func’
    t.reset(new std::thread(std::bind(&boost::asio::io_service::run, &ioService)));
                                                                             ^
In file included from /usr/include/c++/4.9/memory:79:0,
             from test.cpp:2:
/usr/include/c++/4.9/functional:1650:5: note: template<class _Result, class _Func, class ... _BoundArgs> typename std::_Bindres_helper<_Result, _Func, _BoundArgs>::type std::bind(_Func&&, _BoundArgs&& ...)
 bind(_Func&& __f, _BoundArgs&&... __args)
 ^
/usr/include/c++/4.9/functional:1650:5: note:   template argument deduction/substitution failed:
test.cpp:12:80: note:   couldn't deduce template parameter ‘_Result’
    t.reset(new std::thread(std::bind(&boost::asio::io_service::run, &ioService)));
                                                                            ^
Run Code Online (Sandbox Code Playgroud)

我错过了什么?

Tan*_*ury 13

错误消息表明std::bind()无法确定io_service::run()要使用的重载:

std::size io_service::run();
std::size io_service::run(boost::system::error_code&);
Run Code Online (Sandbox Code Playgroud)

对于这种特殊情况,Boost.Bind没有问题,但它确实为绑定重载函数提供了一些故障排除.它建议铸造:

std::bind(
  static_cast<std::size_t (boost::asio::io_service::*)()>(&boost::asio::io_service::run),
  &ioService);
Run Code Online (Sandbox Code Playgroud)

或使用临时变量:

std::size_t (boost::asio::io_service::*run)() = &boost::asio::io_service::run;
std::bind(run, &ioService);
Run Code Online (Sandbox Code Playgroud)

为什么需要为显式类型转换的原因std::bind(),但不能boost::bind()是由于实施细则.如果调用的arity bind()没有对被绑定的函数类型设置约束,则重载函数将需要显式转换.

例如,考虑使用可变参数模板的情况:

template<class F, class... BoundArgs>
unspecified std::bind(F&& f, BoundArgs&&... bound_args);
Run Code Online (Sandbox Code Playgroud)

当选择最佳匹配的std::bind()重载时,调用的arity对其std::bind()没有限制F.作为F可能是下列的:

  • std::size_t (boost::asio::io_service::*)()
  • std::size_t (boost::asio::io_service::*)(boost::system::error_code&)

表达&boost::asio::io_service::run()方式含糊不清.

另一方面,Boost.Bind是用重载函数实现的,其中调用boost::bind()的arity对被绑定函数的arity设置了约束.其界面概要列出了以下值得注意的重载:

// 2 args: member-to-function (arity:0), instance
template <class R, class T, class A1>
unspecified bind(R (T::*f)(), A1 a1);

// 3 args: member-to-function (arity:1), instance, arg1
template <class R, class T, class B1, class A1, class A2>
unspecified bind(R (T::*f)(B1), A1 a1, A2 a2);
Run Code Online (Sandbox Code Playgroud)

注意,当boost::bind()有:

  • arity为2,指向成员的函数的arity为0
  • arity为3,指向成员的函数的arity为1

因此,在致电时:

boost::bind(&boost::asio::io_service::run, &ioService)
Run Code Online (Sandbox Code Playgroud)

boost::bind()潜在匹配的重载具有2的arity,因此指向成员的函数必须是一个arity为0的函数类型.因为只有io_service::run()过载集合中的单个函数的arity为0,所以调用不是暧昧.


Die*_*ühl 10

基于错误消息boost::asio::io_service::run是重载或成员模板,std::bind()并且无法确定它要使用哪个重载或实例化.您需要使用这样的东西,在获取地址时推断出适当的类型,例如,

static_cast<std:size_t (boost::asio::io_service::*)()>(&boost::asio::io_service::run)
Run Code Online (Sandbox Code Playgroud)

关键问题是将boost::asio::io_service::run结果的地址作为成员函数的两个选择,但在获取地址时,没有任何方法可以确定使用哪个.只有在使用成员函数时,才会明确表示不使用其他参数的版本.但是,address-of运算符不会产生过载集.

要推断出需要哪个重载,std::bind()需要提供一个自身的重载,它具有适当的类型作为其第一个参数.但是,我认为它无法确定应该是什么类型.我不知道它是如何实际使用的std::boost::bind()!我可以想像,boost::bind()有成员函数重载专用不带参数,可能的话,一个boost::error_code,但我实在不明白怎么会有用的.

但是,使用lambda函数可能更容易:

std::thread t([&](){ ioService.run(); });
t.join(); // without a joining or detaching terminate() is called
Run Code Online (Sandbox Code Playgroud)