Boost.Asio默认令牌支持导致自由函数调用不明确错误

问题描述 投票:0回答:1

概述

我基于Boost.Asio编写了一些名为

client
的网络客户端类。
client
在内部使用
boost::asio::async_read()
来读取字节直到期望的字节。 当我将默认完成标记支持的代码添加到
client
并使用默认标记时,编译器会报告错误
call to 'async_read' is ambiguous

两个不明确的函数在这里:

https://github.com/boostorg/asio/blob/boost-1.84.0/include/boost/asio/impl/read.hpp#L505 https://github.com/boostorg/asio/blob/boost-1.84.0/include/boost/asio/impl/read.hpp#L525

环境

Linux,x86-64 clang 18.1.0,-std=c++20 升压1.84.0

重现代码

注意:这仅用于检查编译错误。

godbolt 运行演示:https://godbolt.org/z/b344jdWzh

#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) {
#if 1       
            // calling free function causes error: call to 'async_read' is ambiguous
            as::async_read(
                c.next_layer(),
                as::buffer(str),
                std::move(self)
            );
#else
            // calling member function, no error
            c.next_layer().async_read_some(
                as::buffer(str),
                std::move(self)
            );
#endif
        }

        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()};
        // auto c{as::use_awaitable.as_default_on(client<tcp>{ioc.get_executor()})};
        as::co_spawn(
            c.get_executor(),
            coro_test_default(c),
            as::detached
        );
    }
    ioc.run();
}

我尝试了什么

我用成员函数

async_read()
替换了免费函数版本的
async_read_some()
。然后就不会出现编译错误了。

#if 1
替换为
#if 0

即可看到结果
        template <typename Self>
        void operator()(Self& self) {
#if 1       
            // calling free function causes error: call to 'async_read' is ambiguous
            as::async_read(
                c.next_layer(),
                as::buffer(str),
                std::move(self)
            );
#else
            // calling member function, no error
            c.next_layer().async_read_some(
                as::buffer(str),
                std::move(self)
            );
#endif
        }

所以我猜这是一些与自由功能相关的问题。 有什么办法可以解决吗

c++ boost coroutine asio
1个回答
0
投票

是的,这是一个问题。我也偶尔遇到过。它与“自由功能”无关。相反,它与其他重载的存在有关,当让编译器推断默认的完成标记时,这些重载变得不明确。

注意:

ReadToken
的默认值在前向声明头
asio/read.hpp
中声明,而不是编译器报告的定义位置,
asio/impl/read.hpp

在大多数情况下,我已经能够通过明确说明令牌来消除歧义:

asio::async_read(s, b, asio::transfer_all(), asio::deferred);

在通用代码中,您可以通过将 Token 扣除与重载决策分开来实现:

using Token = asio::default_completion_token_t<decltype(s)::executor_type>;
asio::async_read(s, b, asio::transfer_all(), Token());

这是一个最小化的示例:https://godbolt.org/z/36zjdbj8x

报告问题

也许这个最小的例子有点太小了。其他操作似乎遇到相反的情况:显式指定令牌会使重载解析失败,为此目的请考虑以下也练习

asio::async_connect
的示例:

实时编译器资源管理器

#include <boost/asio.hpp>
#include <boost/core/ignore_unused.hpp>
namespace asio = boost::asio;
using asio::ip::tcp;

int main() {
    auto x = asio::system_executor{};
    auto r = asio::deferred.as_default_on(tcp::resolver{x});
    auto s = asio::deferred.as_default_on(tcp::socket{x});

    asio::streambuf b;
    using Token = asio::default_completion_token_t<decltype(s)::executor_type>;

    // auto op1 = asio::async_connect(s, r.resolve("", "8989"), Token());// BROKEN
    auto op1 = asio::async_connect(s, r.resolve("", "8989")); // Workaround

    // but conversely:

    // auto op2 = asio::async_read(s, b, asio::transfer_all()); // BROKEN
    auto op2 = asio::async_read(s, b, asio::transfer_all(), Token()); // Workaround

    boost::ignore_unused(op1, op2);
}
© www.soinside.com 2019 - 2024. All rights reserved.