Search code examples
c++11boostboost-asio

Creating a std::vector of boost::asio timers makes the compilation fail


Both GCC 4.7.3 and 4.8.0 on Coliru fail to compile the following code. It looks like asio timers are not movable:

#include <vector>
#include <chrono>
#include <boost/asio.hpp>
#include <boost/asio/system_timer.hpp>

int main() {
    boost::asio::io_service io;    
    boost::asio::system_timer t{io}; // works
    std::vector<boost::asio::system_timer> timers;
    timers.emplace_back(io); // timer cannot be constrcuted in place
    timers.push_back(std::move(t)); // cannot be moved as well
}

Do you know why and way to create a vector of timers? In my case, one solution would be to use a vector of unique pointers, but I prefer to avoid that.

The error from the compiler is:

 In file included from /usr/include/c++/4.8/vector:62:0,
 
                  from main.cpp:1:
 
 /usr/include/c++/4.8/bits/stl_construct.h: In instantiation of 'void
 std::_Construct(_T1*, _Args&& ...) [with _T1 =
 boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>
 _Args = {boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock,
 boost::asio::wait_traits<std::chrono::_V2::system_clock>,
 boost::asio::waitable_timer_service<std::chrono::_V2::system_clock,
 boost::asio::wait_traits<std::chrono::_V2::system_clock> > >}]':
 
 /usr/include/c++/4.8/bits/stl_uninitialized.h:75:53:   required from
 'static _ForwardIterator
 std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>*>
 _ForwardIterator = boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>*;
 bool _TrivialValueTypes = false]'
 
 /usr/include/c++/4.8/bits/stl_uninitialized.h:117:41:   required from
 '_ForwardIterator std::uninitialized_copy(_InputIterator,
 _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>*>
 _ForwardIterator = boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>*]'
 
 /usr/include/c++/4.8/bits/stl_uninitialized.h:258:63:   required from
 '_ForwardIterator std::__uninitialized_copy_a(_InputIterator,
 _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator<boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>*>
 _ForwardIterator = boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>*;
 _Tp = boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>]'
 
 /usr/include/c++/4.8/bits/stl_uninitialized.h:281:69:   required from
 '_ForwardIterator
 std::__uninitialized_move_if_noexcept_a(_InputIterator,
 _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>*;
 _ForwardIterator = boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>*;
 _Allocator = std::allocator<boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>
 >]'
 
 /usr/include/c++/4.8/bits/vector.tcc:415:43:   required from 'void
 std::vector<_Tp, _Alloc>::_M_emplace_back_aux(_Args&& ...) [with _Args
 = {boost::asio::io_service&}; _Tp = boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>
 _Alloc = std::allocator<boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>
 >]'
 
 /usr/include/c++/4.8/bits/vector.tcc:101:54:   required from 'void
 std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args =
 {boost::asio::io_service&}; _Tp =
 boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>
 _Alloc = std::allocator<boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>
 >]'
 
 main.cpp:10:27:   required from here
 
 /usr/include/c++/4.8/bits/stl_construct.h:75:7: error: use of deleted
 function
 'boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>::basic_waitable_timer(boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>&&)'
 
      { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
 
        ^
 
 In file included from /usr/local/include/boost/asio.hpp:33:0,
 
                  from main.cpp:3:
 
 /usr/local/include/boost/asio/basic_waitable_timer.hpp:127:7: note:
 'boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>::basic_waitable_timer(boost::asio::basic_waitable_timer<std::chrono::_V2::system_clock>&&)'
 is implicitly deleted because the default definition would be
 ill-formed:
 
  class basic_waitable_timer
 
        ^
 
 In file included from
 /usr/local/include/boost/asio/basic_socket.hpp:20:0,
 
                  from /usr/local/include/boost/asio/basic_datagram_socket.hpp:20,
 
                  from /usr/local/include/boost/asio.hpp:21,
 
                  from main.cpp:3:
 
 /usr/local/include/boost/asio/basic_io_object.hpp:163:3: error:
 'boost::asio::basic_io_object<IoObjectService,
 Movable>::basic_io_object(const
 boost::asio::basic_io_object<IoObjectService, Movable>&) [with
 IoObjectService =
 boost::asio::waitable_timer_service<std::chrono::_V2::system_clock,
 boost::asio::wait_traits<std::chrono::_V2::system_clock> > bool
 Movable = false]' is private
 
    basic_io_object(const basic_io_object&);
 
    ^
 
 In file included from /usr/local/include/boost/asio.hpp:33:0,
 
                  from main.cpp:3:
 
 /usr/local/include/boost/asio/basic_waitable_timer.hpp:127:7: error:
 within this context
 
  class basic_waitable_timer
 
        ^

Solution

  • ASIO hasn't been updated with move semantics yet (or at least parts of it), so the timer isn't movable. So your choices are

    • Use unique_ptr.
    • Use a container that doesn't require elements to be movable for all operations, like deque or list.

    That would look like this:

    #include <deque>
    #include <chrono>
    #include <boost/asio.hpp>
    #include <boost/asio/system_timer.hpp>
    
    int main() {
        boost::asio::io_service io;    
        boost::asio::system_timer t{io}; // works
        std::deque<boost::asio::system_timer> timers;
        timers.emplace_back(io); // should work
        timers.push_back(std::move(t)); // won't work
    }
    

    But don't try to use insert or erase for the collection, because those require movability.