You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 16:47:10