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.
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:
All option is -std=c++14
g++ 7.1.0 or later : 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
Output A
->
->
move
move
move
move
Success
Output B
->
move
->
Segmentation fault
It seems that clang++ and g++ 7.1.0 or later evaluates tim->async_wait()
first.
g++ 6.3.0 evaluates tim = std::move(tim)
first.
Is this an undefined behavior? Or evaluation order is defined at some point?