【问题标题】:Cannot store bind_front_handler return value in variable无法将 bind_front_handler 返回值存储在变量中
【发布时间】:2021-05-15 00:40:14
【问题描述】:

这些存在于我的代码库的其他部分,

namespace net = boost::asio;
using boost::asio::ip::tcp;

boost::asio::io_context& io_context_;
tcp::acceptor acceptor_;
void server::on_accept(boost::beast::error_code ec, boost::asio::ip::tcp::socket socket);

我注意到这段代码可以编译:

auto strand = net::make_strand(io_context_);
std::shared_ptr<server> this_pointer = shared_from_this();

acceptor_.async_accept(
    strand,
    boost::beast::bind_front_handler(&server::on_accept, this_pointer)
);

而这不是:

auto strand = net::make_strand(io_context_);
std::shared_ptr<server> this_pointer = shared_from_this();

auto call_next = boost::beast::bind_front_handler(&server::on_accept, this_pointer);

acceptor_.async_accept(
    strand,
    call_next
);

它失败并出现错误

/usr/include/boost/beast/core/detail/bind_handler.hpp:251:45: error: cannot convert ‘boost::beast::detail::bind_front_wrapper<void (server::*)(boost::system::error_code, boost::asio::basic_stream_socket<boost::asio::ip::tcp>), std::shared_ptr<server> >’ to ‘void (server::*)(boost::system::error_code, boost::asio::basic_stream_socket<boost::asio::ip::tcp>)’ in initialization
  251 |         , args_(std::forward<Args_>(args)...)

我很好奇为什么将 bind_front_handler 返回的值直接传递给 async_accept 会起作用,但将该值存储在变量中然后传递该变量将不起作用。

我现在对 Boost 和 Beast 也知之甚少,但在我看来,我好像忘记了关于 C++ 本身的一些非常基本的东西。为什么这两段代码不等价?

【问题讨论】:

    标签: c++ boost stdbind boost-beast


    【解决方案1】:

    确实,您不应该这样做。 bind-front 包装器希望 是临时的(因为它只是移动)。你可以通过这样做来“修复”它

        acceptor_.async_accept(strand, std::move(call_next));
    

    (之后您必须记住,call_next 可能无法再次使用,因为它已被移出)。

    我个人会走另一条路——因为这个助手显然是有意的——写惯用语

        acceptor_.async_accept(
            make_strand(io_context_),
            bind_front_handler(&server::on_accept, shared_from_this()));
    

    替换整个函数。

    演示

    Live On Coliru

    #include <boost/beast.hpp>
    #include <boost/asio.hpp>
    #include <iostream>
    namespace net = boost::asio;
    namespace beast = boost::beast;
    using boost::system::error_code;
    using net::ip::tcp;
    
    struct server : std::enable_shared_from_this<server> {
        server() {
            acceptor_.listen();
        }
    
        void start(){
            using beast::bind_front_handler;
            acceptor_.async_accept(
                make_strand(io_context_),
                bind_front_handler(&server::on_accept, shared_from_this()));
        }
    
        void wait() {
            work_.reset();
    
            if (thread_.joinable())
                thread_.join();
        }
    
      private:
        void on_accept(error_code ec, tcp::socket&& socket) {
            std::cout << "Accepted connection from " << socket.remote_endpoint() << "\n";
            //// loop to accept more:
            // start();
        }
    
        net::io_context io_context_;
        tcp::acceptor   acceptor_{io_context_, {{}, 9999}};
    
        net::executor_work_guard<net::io_context::executor_type> work_{
            io_context_.get_executor()};
        std::thread thread_{[this] { io_context_.run(); }};
    };
    
    int main()
    {
        auto s = std::make_shared<server>();
        s->start();
        s->wait();
    }
    

    g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp
    ./a.out& sleep .5; nc 127.0.0.1 9999 <<<'hello world'; wait
    

    打印例如

    Accepted connection from 127.0.0.1:36402
    

    【讨论】:

    • 谢谢!我并没有以惯用的方式来理解它是如何工作的。因此,在 C++ 中,如果函数参数之一是 r 值引用 (&&),并且在调用该函数时我们创建了一个对象,它不会在调用者的作用域中创建然后被复制,而只是在该被调用函数的作用域中创建。为什么bind的返回值不想被复制有一个简单的原因吗?
    • 阅读更多关于std::move的信息,我感觉上面的评论可能不正确。
    • 确实如此。右值引用仅表明一个值可能是可移动的——这意味着它比其他方式为临时值传递值更便宜。撤消 C++ 默认值语义类型系统的缺点和直接相关的确定性破坏,这在幕后有点巫术。 std:::move 是 C++11 变化的基础,仅仅通过重新编译就在一夜之间赢得了很多程序的“免费性能”。这可能也是处理程序包装器坚持只移动 - 效率的原因。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-24
    • 2015-11-09
    • 2013-09-03
    • 2013-02-07
    • 1970-01-01
    • 2017-01-07
    • 2022-11-17
    相关资源
    最近更新 更多