Lambda表达移动捕获的时间 [英] Timing of lambda expression move capture

查看:117
本文介绍了Lambda表达移动捕获的时间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

When I use Boost.Asio, creating object such as ip::tcp::socket or deadline_timer as std::shared_ptr and copy captured it to the completion handler as lambda expression.

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

I curious that what happens if I use move capture instead of copy capture. I think that it is dangerous. In the following example, I think that tim = std::move(tim) is evaluated before tim->async_wait. So tim no longer has valid pointer. It is my guess. In order to trace std::shared_ptr's behavior, I created std::shared_ptr wrapper 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();
}

我在多个环境中运行代码:

I run the code on several environment:

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

g ++ 7.1.0或稍后: https://wandbox.org/permlink/rgJLp66vH7jKodQ8 A

g++ 7.1.0 or later : https://wandbox.org/permlink/rgJLp66vH7jKodQ8 A

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

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

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

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()首先。

It seems that clang++ and g++ 7.1.0 or later evaluates tim->async_wait() first.

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

g++ 6.3.0 evaluates tim = std::move(tim) first.

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

Is this an undefined behavior? Or evaluation order is defined at some point?

推荐答案

C ++ 17中的评估顺序定义明确,例如< a href = https://timsong-cpp.github.io/cppwp/n4659/expr.call#5 rel = nofollow noreferrer>导致函数调用的表达式( tim-> ; async_wait )在其任何参数之前进行排序。

The evaluation order in C++17 is well-defined, such that the expression leading to the function call (tim->async_wait) is sequenced before any of its arguments.

C ++ 14,但是由于< a href = https://timsong-cpp.github.io/cppwp/n4140/expr.call#8 rel = nofollow noreferrer>缺少此类排序规则。也就是说,它可能会起作用,也可能不会起作用,不需要实现告诉您选择哪种方法,甚至不需要从一个调用到另一个调用保持一致。

C++14 however, this is unspecified, due to lack of such sequencing rules. That is, it may work, it may not, and implementations aren't required to tell you which way it picks, or even to be consistent from one call to another.

这篇关于Lambda表达移动捕获的时间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆