Boost.Asio默认Completion Token引发async_read自由函数调用歧义
问题描述
基于Boost.Asio实现client网络客户端类,内部使用boost::asio::async_read()读取指定字节数。为该类添加默认完成令牌支持后,使用默认令牌时编译器报错:call to 'async_read' is ambiguous,歧义来自async_read的两个重载版本。
环境
- Linux x86-64
- clang 18.1.0
- 编译标准:
-std=c++20 - Boost版本:1.84.0
复现代码
以下代码可复现编译错误:
#include <string> #include <boost/asio.hpp> namespace as = boost::asio; std::string str; template <typename NextLayer> struct client { // type aliases using next_layer_type = NextLayer; using executor_type = typename next_layer_type::executor_type; // constructor template <typename... Args> explicit client(Args&&... args):nl{std::forward<Args>(args)...}{} // rebind constructor for default token template <typename Other> explicit client(client<Other>&& other):nl{std::move(other.nl)} {} // accessor next_layer_type const& next_layer() const { return nl; }; next_layer_type& next_layer() { return nl; }; executor_type get_executor() { return nl.get_executor(); } // async_func template < typename CompletionToken = as::default_completion_token_t<executor_type> > auto async_read_packet( CompletionToken&& token = as::default_completion_token_t<executor_type>{} ) { return as::async_compose< CompletionToken, void(boost::system::error_code, std::size_t) >( read_packet_op{ *this }, token ); } // async_func impl struct read_packet_op { client& c; template <typename Self> void operator()(Self& self) { // calling free function causes error: call to 'async_read' is ambiguous as::async_read( c.next_layer(), as::buffer(str), std::move(self) ); } template <typename Self> void operator()(Self& self, boost::system::error_code ec, std::size_t size) { self.complete(ec, size); } }; // rebind for default token template <typename Executor1> struct rebind_executor { using other = client< typename NextLayer::template rebind_executor<Executor1>::other >; }; // member variables next_layer_type nl; }; as::awaitable<void> coro_test(auto& c) { auto size = co_await c.async_read_packet(as::use_awaitable); (void)size; } as::awaitable<void> coro_test_default(auto& c) { auto size = co_await c.async_read_packet(); (void)size; } int main() { using tcp = as::basic_stream_socket<as::ip::tcp, as::any_io_executor>; as::io_context ioc; { // no default token version client<tcp> c{ioc.get_executor()}; as::co_spawn( c.get_executor(), coro_test(c), as::detached ); } { // default token version using default_token = boost::asio::as_tuple_t<boost::asio::use_awaitable_t<>>; using def_client = default_token::as_default_on_t<client<tcp>>; def_client c{ioc.get_executor()}; as::co_spawn( c.get_executor(), coro_test_default(c), as::detached ); } ioc.run(); }
已尝试方案
将自由函数async_read()替换为成员函数async_read_some()后,编译错误消失,修改后的代码片段如下:
template <typename Self> void operator()(Self& self) { // calling member function, no error c.next_layer().async_read_some( as::buffer(str), std::move(self) ); }
解决方法
出现歧义的核心原因是:使用默认令牌包装后的流类型时,async_compose生成的Self完成处理程序类型,同时满足两个async_read重载的推导条件——一个是仅接受流、缓冲区、完成处理程序的版本,另一个是接受流、缓冲区、完成条件、完成处理程序的版本,编译器无法明确选择重载。
可通过以下方式消除歧义:
方法一:显式指定完成条件
在调用async_read时,显式传递完成条件(如asio::transfer_exactly或asio::transfer_all),让编译器匹配到带完成条件的重载:
template <typename Self> void operator()(Self& self) { as::async_read( c.next_layer(), as::buffer(str), as::transfer_exactly(str.size()), // 显式指定完成条件 std::move(self) ); }
方法二:显式限定重载类型
通过static_cast强制指定要调用的重载版本,明确匹配三参数版本:
template <typename Self> void operator()(Self& self) { using AsyncReadFunc = decltype(&as::async_read<decltype(c.next_layer()), as::mutable_buffer, Self>); static_cast<AsyncReadFunc>(&as::async_read)( c.next_layer(), as::buffer(str), std::move(self) ); }
内容的提问来源于stack exchange,提问作者Takatoshi Kondo
相关产品推荐
相关产品推荐

