简体   繁体   中英

Why does boost::asio::io_service not compile with std::bind?

I'm trying to compile simple test program with std::thread , std::bind and boost::asio using g++ 4.9.1 ( -std=c++11 ).

However, when creating new thread, it doesn't compile, when I use std::bind . On the other hand, when I switch to boost::bind everything is fine.

Here's the code:

#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;
}

Here's the error:

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)));
                                                                            ^

What am I missing?

The error message indicate that std::bind() cannot determine which io_service::run() overload to use:

std::size io_service::run();
std::size io_service::run(boost::system::error_code&);

For this particular case, Boost.Bind does not have an issue, but it does provide some troubleshooting for binding an overloaded functions . It recommends either casting:

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

Or using a temporary variable:

std::size_t (boost::asio::io_service::*run)() = &boost::asio::io_service::run;
std::bind(run, &ioService);

The reason why the explicit casting is needed for std::bind() but not boost::bind() is due to implementation details. If the arity of the call to bind() does not place constraints on the type of function being binded, then explicit casting will be required for overloaded functions.

For instance, consider the case where a variadic template is used:

template<class F, class... BoundArgs>
unspecified std::bind(F&& f, BoundArgs&&... bound_args);

When the best matching std::bind() overload is being selected, the arity of the call to std::bind() places no restrictions on F . As F could be either of the following:

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

the expression &boost::asio::io_service::run() is ambiguous.

On the other hand, Boost.Bind is implemented with overloaded functions wherein the arity of the call to boost::bind() places constraints on the arity of the function being binded. Its interface synopsis lists the following noteworthy overloads:

// 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);

Note that when boost::bind() has:

  • an arity of 2, the pointer-to-member function has an arity of 0
  • an arity of 3, the pointer-to-member function has an arity of 1

Hence, when calling:

boost::bind(&boost::asio::io_service::run, &ioService)

The boost::bind() overloads that are potential matches have an arity of 2, so the pointer-to-member function must be a type of function with an arity of 0. As only a single function in the set of io_service::run() overloads has an arity of 0, the call is not ambiguous.

Based on the error message boost::asio::io_service::run is overloaded or member template and std::bind() can't determine which overload or instantiation it is meant to use. You'll need to use something like this which deduces the appropriate type when taking the address, eg,

static_cast<std:size_t (boost::asio::io_service::*)()>(&boost::asio::io_service::run)

The key issue is that taking the address of boost::asio::io_service::run results in two choices for the member function but at the time the address is taken there isn't any way to determine which one to use. Only when the member function is used it is clear that the version taking no additional arguments is meant to be used. The address-of operator won't produce an overload set, however.

To deduce which overload is needed, std::bind() would need to provide an overload of itself which has the appropriate type as its first argument. However, it has no way to determine what that type should be, I think. I don't know how it actually works with std::boost::bind() ! I could imagine that boost::bind() has special overloads for member functions with no argument and, possibly, one for boost::error_code but I don't really see how that would help.

It may be easier to use a lambda function, though:

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

the key to understanding this error is the unresolved overloaded function type part

this means the compiler couldnt figure out which overloaded version of boost::asio::io_service::run to use.

Looking up in the docs one see there are 2 versions, you want to use the std::size_t run() one. To communicate this to the compiler we need to static_cast the function pointer to explicit type of the overloaded variant, here std:size_t (boost::asio::io_service::*)()

hence we write static_cast<std::size_t (boost::asio::io_service::*)(void)>(&boost::asio::io_service::run) in place of just &boost::asio::io_service::run

the full code looks like this then

boost::asio::io_service ioService;
std::unique_ptr<std::thread> t;

t.reset(new std::thread(std::bind(
    static_cast<std::size_t(boost::asio::io_service::*)(void)>(&boost::asio::io_service::run),
    &ioService
)));

Boost Bind supports smart pointers bound as the this argument for bound member functions.

This is a rather big (and awesome) difference between std::bind and boost::bind.

Boost Asio has always promoted patterns¹ that rely heavily on binding to shared_pointer<T> where T can be anything with "magically managed" lifetime in the context of asynchronous IO, such as connection , client , session , transfer etc.

Of course c++11 lambdas can support the same directly (by capturing a shared pointer by copy).


¹ eg

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM