Lambda表达移动捕获的时间

时间:2019-07-19 08:12:22

标签: c++ c++14 sequence undefined-behavior evaluation

当我使用Boost.Asio时,将ip::tcp::socket之类的对象创建为deadline_timerstd::shared_ptr,然后将其作为lambda表达式复制捕获到完成处理程序中。

我很好奇,如果我使用移动捕获而不是复制捕获会发生什么。我认为这很危险。在下面的示例中,我认为tim = std::move(tim)tim->async_wait之前被评估。因此,tim不再具有有效的指针。这是我的猜测。为了跟踪std::shared_ptr的行为,我创建了std::shared_ptr包装器shared_ptr

#include <iostream>

#include <boost/asio.hpp>

namespace as = boost::asio;

template <typename... Args>
struct shared_ptr : std::shared_ptr<Args...> {
    using base = std::shared_ptr<Args...>;
    using base::base; // inheriting constructor

    shared_ptr(shared_ptr&& other) : base(std::move(other)) {
        std::cout << "move" << std::endl;
    }
    typename base::element_type* operator->() {
        std::cout << "->" << std::endl;
        return base::get();
    }
};

int main() {
    as::io_context ioc;

    ioc.post( 
        [&] {
            shared_ptr<as::deadline_timer> tim(new as::deadline_timer(ioc));
            tim->expires_from_now(boost::posix_time::seconds(1));
            tim->async_wait( 
                // I think that it is dangerous because tim has been moved before tim->async_wait()
                [&, tim = std::move(tim)] 
                    std::cout << ec.message() << std::endl;
                }
            );
        }
    );

    ioc.run();
}

我在几种环境中运行代码:

所有选项均为-std=c++14

g ++ 7.1.0或更高版本:https://wandbox.org/permlink/rgJLp66vH7jKodQ8 A

g ++ 6.3.0:https://wandbox.org/permlink/XTIBhjJSqmkQHN4P B

clang ++ 4.0.1〜:https://wandbox.org/permlink/nEZpFV874pKstjHA A

输出A

->
->
move
move
move
move
Success

输出B

->
move
->
Segmentation fault

似乎clang ++和g ++ 7.1.0或更高版本首先评估tim->async_wait()

g ++ 6.3.0首先评估tim = std::move(tim)

这是未定义的行为吗?还是在某个时候定义了评估顺序?

1 个答案:

答案 0 :(得分:-1)

C ++ 17中的评估顺序是明确定义的,例如the expression leading to the function call (tim->async_wait) is sequenced before any of its arguments

C ++ 14,由于lack of such sequencing rules,因此未指定。也就是说,它可能行得通,也可能行不通,并且不需要实现告诉您选择哪种方法,甚至不需要从一个调用到另一个调用保持一致。