C++ lambda表达式移动捕获的计时
当我使用Boost.Asio时,创建对象,如C++ lambda表达式移动捕获的计时,c++,c++14,sequence,undefined-behavior,evaluation,C++,C++14,Sequence,Undefined Behavior,Evaluation,当我使用Boost.Asio时,创建对象,如ip::tcp::socket或deadline\u timer作为std::shared\u ptr,并将捕获的对象作为lambda表达式复制到完成处理程序 我很好奇如果我使用移动捕获而不是复制捕获会发生什么。我认为这很危险。在下面的示例中,我认为tim=std::move(tim)是在tim->async\u wait之前计算的。因此,tim不再具有有效指针。这是我的猜测。为了跟踪std::shared_ptr的行为,我创建了std::shared
ip::tcp::socket
或deadline\u timer
作为std::shared\u ptr
,并将捕获的对象作为lambda表达式复制到完成处理程序
我很好奇如果我使用移动捕获而不是复制捕获会发生什么。我认为这很危险。在下面的示例中,我认为tim=std::move(tim)
是在tim->async\u wait
之前计算的。因此,tim不再具有有效指针。这是我的猜测。为了跟踪std::shared_ptr
的行为,我创建了std::shared_ptr
wrappershared_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();
}
输出B
->
move
->
Segmentation fault
似乎clang++和g++7.1.0或更高版本首先计算tim->async\u wait()
g++6.3.0首先计算tim=std::move(tim)
这是一种未定义的行为吗?或者在某个点定义了求值顺序?C++17中的求值顺序定义良好,因此
但是,这是未指定的,因为。也就是说,它可以工作,也可以不工作,而且实现不需要告诉您它选择哪种方式,甚至不需要从一个调用到另一个调用保持一致。我目前没有标准的措辞,但我很确定这是一种未定义的行为,因为您是tim->async\u wait
call作用于已从中移动的对象。谢谢您的回答。这意味着如果编译器完全支持C++17,tim=std::move(tim)
是更好的选择。由于共享计数未更改,因此它的性能(速度)高于复制实现。是吗?@TakatoshiKondo:在这种情况下,性能是不相关的,因为您无论如何都要执行异步分派操作。我怀疑原子的增量/减量是否会在性能方面产生差异。你的意思是你认为原子的增量/减量总体上会产生性能差异,但在这种情况下,它不是主要因素。我理解正确吗?如果它是一个成员函数,tim->async_wait
在其参数之前排序并不重要-在move
已经发生之后,它仍然会用pre-movethis
指针调用。@Eric:如果它在之前排序,这与它的工作方式无关。事实上,在开始工作之前,需要对其进行排序。问题是它是否在之后排序。计算参数后,tim
不再具有有效指针。因此,您必须在计算参数之前获取指针,否则该操作将不起作用。
->
move
->
Segmentation fault