Lambda表达移动捕获的时间 [英] Timing of lambda expression move capture
问题描述
当我使用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屋!