Can lambda capture-by-value be optimized out?
Asked Answered
M

1

5

I'm currently using boost::asio for a project, and have to send buffers to remote endpoints. My current algorithm for sending the data looks like this:

void send_the_data(DataElement const& data)
{
    auto databuf = make_shared<std::vector<uint8_t>>(data.to_bytes());

    // lambda object holds a reference to the data to prevent early destruction. 
    asio::async_write(this->sock,
        asio::buffer(databuf),
        transfer_all(),
        [this, databuf](size_t bt, boost::system::error_code const& ec)
    {
        if(ec) this->handle_error(ec);
        else this->do_the_next_thing();

        assert(bt = databuf->size());
        // the destructor of this lambda should clean up the data buffer here,
        // after it has been handled. 
    });

}

My logic is that the lambda capture of the shared_ptr will prevent it from being destroyed until after the async_write is done with it, and then properly clean up the buffer once the handler has executed.

However, I'm curious if either the major compilers or the standard allow for the capture of the variable to be elided if there is no reference to it in the body of the lambda, which would lead to undefined behaviour (due to possibly accessing a dangling pointer within the async_write call), or if the standard guarantees that all value captures will not be elided.

Mammiemammiferous answered 6/8, 2018 at 17:22 Comment(2)
They should not if you do an explicit capture of that variable. It could contain the memory another variable refers toAcred
AFAIK it can't be optimized away, because as-if rule (which doesn't have exceptions for lambda captures).Oosphere
L
8

While [expr.prim.lambda] §2 would theoretically allow compilers to optimize the closure type, such optimization is only allowed under the as-if rule. So a compiler could optimize away unreferenced data from the closure type, but it would still have to produce any side effects associated with the construction/destruction of the respective members.

[expr.prim.lambda] §10 specifies that for each explicit capture, a member of the closure type is created. [expr.prim.lambda] §15 specifies how it is initialized. Based on this, I would say that a compiler is not allowed to optimize away your shared_ptr.

Leper answered 6/8, 2018 at 17:34 Comment(2)
A unique_ptr could not be used here since the completion handler must be CopyConstructible (boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/…) on boost versions before 1.67. (and sadly my linux distribution only ships 1.58 at this time.)Mammiemammiferous
Ok, I'll remove that bit then…Leper

© 2022 - 2024 — McMap. All rights reserved.