问题
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?
回答1:
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 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.
来源:https://stackoverflow.com/questions/57108220/timing-of-lambda-expression-move-capture