从C++17升级到C++20时Boost asio async_accept配合use_future编译报错
环境信息
- OS:Ubuntu 22.04
- 编译器:gcc 11.3
- Boost版本:1.80
问题描述
项目从C17升级至C20后,调用acceptor_.async_accept并传入boost::asio::use_future作为第二个参数时出现编译错误,C++17环境下编译正常。修改Boost官方示例(将原回调逻辑替换为boost::asio::use_future)可复现该问题。
具体报错信息
/usr/include/c++/11/type_traits:809:68: error: ‘boost::asio::basic_socket<Protocol, Executor>::~basic_socket() [with Protocol = boost::asio::ip::tcp; Executor = boost::asio::any_io_executor]’ is protected within this context
809 | template<typename _Tp, typename = decltype(declval<_Tp&>().~_Tp())>
| ~~~~~~~~~~~~~~~~~~~~^~
/include/boost/asio/basic_socket.hpp:1845:3: note: declared protected here
1845 | ~basic_socket()
原因分析
C20对模板类型检查的严格程度高于C17,boost::asio::use_future的内部实现会尝试构造或析构basic_socket对象,但basic_socket的析构函数被声明为protected——它作为基类,仅允许boost::asio::ip::tcp::socket这类派生类访问。在C20的类型推导规则下,这种跨层级的访问触发了保护权限检查,而C17的规则未检测到该问题。
解决方案
使用派生类类型接收连接socket
确保调用async_accept时,传入的socket对象是boost::asio::ip::tcp::socket而非基类basic_socket。示例代码:boost::asio::ip::tcp::acceptor acceptor_(io_context); boost::asio::ip::tcp::socket socket_(io_context); // 正确用法:传入tcp::socket对象 + use_future auto future = acceptor_.async_accept(socket_, boost::asio::use_future);升级Boost版本
Boost 1.80存在C20下use_future与async_accept的兼容性问题,升级至Boost 1.81及以上版本可修复该类编译错误——官方已针对C20的类型检查调整了use_future的实现逻辑。临时兼容方案(保留use_future功能)
若暂时无法升级Boost,可手动封装适配的future回调替代原生use_future,避免直接触发基类析构的访问问题:template<typename Acceptor> auto make_adapted_future(Acceptor& acceptor) { return [&acceptor](auto&& token) { return boost::asio::async_compose<decltype(token), void(boost::system::error_code, boost::asio::ip::tcp::socket)>( [&acceptor](auto&& self) { acceptor.async_accept( boost::asio::ip::tcp::socket(acceptor.get_executor()), std::move(self) ); }, token, acceptor); }; } // 使用方式 auto future = make_adapted_future(acceptor_)(boost::asio::use_future);
内容的提问来源于stack exchange,提问作者Nick

