【发布时间】:2019-07-19 08:12:22
【问题描述】:
当我使用 Boost.Asio 时,将 ip::tcp::socket 或 deadline_timer 等对象创建为 std::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/rgJLp66vH7jKodQ8A
g++ 6.3.0:https://wandbox.org/permlink/XTIBhjJSqmkQHN4PB
clang++ 4.0.1~ : https://wandbox.org/permlink/nEZpFV874pKstjHAA
输出 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)。
这是未定义的行为吗?还是在某个时候定义了评估顺序?
【问题讨论】:
-
我目前没有标准的措辞,但我很确定这是未定义的行为,因为您的
tim->async_wait调用作用于已被移动的对象。
标签: c++ c++14 sequence undefined-behavior evaluation