C++17和异步成员函数使用move-capture lambda表达式调用
在我的提问中,我了解到一些评估顺序是自C++17以来定义良好的。后缀表达式,如a->f…和a.b…是它们的一部分。看 在中,以下样式的异步成员函数调用是典型的模式C++17和异步成员函数使用move-capture lambda表达式调用,c++,boost,c++17,move,asio,C++,Boost,C++17,Move,Asio,在我的提问中,我了解到一些评估顺序是自C++17以来定义良好的。后缀表达式,如a->f…和a.b…是它们的一部分。看 在中,以下样式的异步成员函数调用是典型的模式 auto sp_object = std::make_shared<object>(...); sp_object->async_func( params, [sp_object] (boost::syste_error_code const&e, ...) { if
auto sp_object = std::make_shared<object>(...);
sp_object->async_func(
params,
[sp_object]
(boost::syste_error_code const&e, ...) {
if (e) return;
sp_object->other_async_func(
params,
[sp_object]
(boost::syste_error_code const&e, ...) {
if (e) return;
// do some
}
);
}
);
我认为这是危险的,因为即使是后缀表达式。在object=std::moveobject之前求值,async_func可以访问object的成员
案例3:共享ptr移动和自由功能
这种模式就像
我认为这很危险,因为没有后缀表达式。因此,sp_对象可以通过第三个参数move capture移动,然后通过第一个参数作为*sp_对象解除引用
结论
只有情况1是安全的,其他情况是危险的未定义行为。
我需要注意的是,它在C++14和更旧的编译器上是不安全的。
它可以加快调用异步成员函数的速度,因为shared_ptr的原子计数器操作没有发生。看见
但我也需要考虑这个优点可以忽略,这取决于应用程序。
我是否正确理解C++17评估顺序更改的精确定义和异步操作关系?答案
多亏了探险家的评论。我得到了答案
我问过案例1是安全的,但案例2和案例3是不安全的,是吗?。然而,当且仅当满足我稍后编写的约束*1时,Case1是安全的。这意味着案例1通常是不安全的
这取决于异步函数
这是一个不安全的案例:
#include <iostream>
#include <memory>
#include <boost/asio.hpp>
struct object : std::enable_shared_from_this<object> {
object(boost::asio::io_context& ioc):ioc(ioc) {
std::cout << "object constructor this: " << this << std::endl;
}
template <typename Handler>
void async_func(Handler&& h) {
std::cout << "this in async_func: " << this << std::endl;
h(123); // how about here?
std::cout << "call shared_from_this in async_func: " << this << std::endl;
auto sp = shared_from_this();
std::cout << "sp->get() in async_func: " << sp.get() << std::endl;
}
template <typename Handler>
void other_async_func(Handler&& h) {
std::cout << "this in other_async_func: " << this << std::endl;
h(123); // how about here?
std::cout << "call shared_from_this in other_async_func: " << this << std::endl;
auto sp = shared_from_this();
std::cout << "sp->get() in other_async_func: " << sp.get() << std::endl;
}
boost::asio::io_context& ioc;
};
int main() {
boost::asio::io_context ioc;
auto sp_object = std::make_shared<object>(ioc);
sp_object->async_func(
[sp_object = std::move(sp_object)]
(int v) mutable { // mutable is for move
std::cout << v << std::endl;
sp_object->other_async_func(
[sp_object = std::move(sp_object)]
(int v) {
std::cout << v << std::endl;
}
);
}
);
ioc.run();
}
正在运行演示:
案例1的约束是安全的
*一,
然而,在案例1中,如果且仅当struct对象不期望它被shared_ptr持有,那么它是安全的。换句话说,只要struct对象不使用这个机制中的shared_,它就是安全的
控制顺序的另一种方法。支持C++14
当且仅当满足上述约束时,我们可以在不使用C++17序列定义的情况下控制求值序列。
它支持案例1和案例3。只需获取由shared_ptr持有的指针对象的引用。关键的一点是,即使移动了共享的ptr,指针对象也会被保留。因此,在移动共享\u ptr之前获取指针对象的引用,然后移动共享\u ptr,指针对象不受影响
然而,这是一个例外情况。它直接使用共享的ptr机制。因此,这受共享ptr移动的影响。因此它是不安全的。这就是限制的原因
案例1
案例3
您的问题可以大大简化为以下安全问题:
some_object.foo([bound_object = std::move(some_object)]() {
bound_object.bar()
});
从你的链接问题中
参数求值的所有副作用在输入函数之前都按顺序排列
其中一个副作用是从某个对象移动,因此这相当于:
auto callback = [bound_object = std::move(some_object)]() {
bound_object.bar()
}
some_object.foo(std::move(callback));
显然,在调用foo方法之前,它会移出一些_对象。这是安全的,当且仅当对已从对象移动的对象调用foo时
利用这些知识:
案例1可能会出现segfault,而且肯定不安全,因为在从共享移动的\u ptr上调用操作符->会返回null ptr,然后调用它->异步\u func on。
只有在从某个类型移动的对象上调用async_func是安全的情况下,情况2才是安全的,但除非该类型实际上没有定义移动构造函数,否则它不太可能达到您的目的。
案例3是不安全的,因为在取消引用之后移动共享指针是可以的,因为共享指针不改变它指向的对象,C++不能保证首先对哪个函数参数进行评估。
首先,你为什么认为案例1是安全的?虽然sp_object->async_func在sp_object=std::movesp_object之前计算过……它是否未定义完全取决于async_func的定义,但您认为当async_func从此访问共享_时会发生什么?因为sp_object=std::movesp_object保留相同的指针对象。移到sp_object.get返回与此对象相同的地址对象is async_func。因此,我相信,从_分享_这很好。这是一个移动共享ptr的工作示例,这是std必须说的10个移动从r构造一个共享ptr。在构造之后,*它包含以前状态r的副本,r为空,其存储的指针为null。如果Y*在C++17兼容之前不能隐式转换为,那么模板重载不会参与重载解析,因为C++17 t*…所以不确定您看到的是定义良好的。我认为这意味着在sp_object->被替换为sp_object.get的地址。假设地址是addr_object。这意味着它被替换为addr\u object->async\u read。替换后,sp_对象变为空。但这没问题。我稍微改变了一下你的例子,它的行为取决于我们调用的函数的功能,如果我遗漏了什么,请告诉我。获取了以类型为std::\u 1::bad\u-weak\u-ptr:bad\u-weak\u-ptr的未捕获异常终止的此操作
你是说第一个代码和第二个代码是等效的吗?更新后的代码明确涵盖了cases@TakatoshiKondo您可以接受这一点作为答案,它清楚地说明了在执行之前进行评估,对我来说这听起来很关键。这是正在运行的代码:如果使用moved from对象调用foo不安全,则它是不安全的。我同意这一点。这是我问题的第二种情况。在案例1中,一些对象是共享的。看看是否并且仅当某个_类不希望我被shared_ptr持有时,我认为这是安全的。我认为有上述限制的案例1是安全的。这就是我的观点。@TakatoshiKondo:你说得对,基于这个理由,案例1看起来还是安全的
#include <iostream>
#include <memory>
#include <boost/asio.hpp>
struct object : std::enable_shared_from_this<object> {
object(boost::asio::io_context& ioc):ioc(ioc) {
std::cout << "object constructor this: " << this << std::endl;
}
template <typename Handler>
void async_func(Handler&& h) {
std::cout << "this in async_func: " << this << std::endl;
h(123); // how about here?
std::cout << "call shared_from_this in async_func: " << this << std::endl;
auto sp = shared_from_this();
std::cout << "sp->get() in async_func: " << sp.get() << std::endl;
}
template <typename Handler>
void other_async_func(Handler&& h) {
std::cout << "this in other_async_func: " << this << std::endl;
h(123); // how about here?
std::cout << "call shared_from_this in other_async_func: " << this << std::endl;
auto sp = shared_from_this();
std::cout << "sp->get() in other_async_func: " << sp.get() << std::endl;
}
boost::asio::io_context& ioc;
};
int main() {
boost::asio::io_context ioc;
auto sp_object = std::make_shared<object>(ioc);
sp_object->async_func(
[sp_object = std::move(sp_object)]
(int v) mutable { // mutable is for move
std::cout << v << std::endl;
sp_object->other_async_func(
[sp_object = std::move(sp_object)]
(int v) {
std::cout << v << std::endl;
}
);
}
);
ioc.run();
}
#include <iostream>
#include <memory>
#include <boost/asio.hpp>
struct object : std::enable_shared_from_this<object> {
object(boost::asio::io_context& ioc):ioc(ioc) {
std::cout << "object constructor this: " << this << std::endl;
}
template <typename Handler>
void async_func(Handler&& h) {
std::cout << "this in async_func: " << this << std::endl;
ioc.post(
[this, h = std::forward<Handler>(h)] () mutable {
h(123);
sleep(1);
auto sp = shared_from_this();
std::cout << "sp->get() in async_func: " << sp.get() << std::endl;
}
);
}
template <typename Handler>
void other_async_func(Handler&& h) {
std::cout << "this in other_async_func: " << this << std::endl;
ioc.post(
[this, h = std::forward<Handler>(h)] () {
h(456);
auto sp = shared_from_this();
std::cout << "sp->get() in other_async_func: " << sp.get() << std::endl;
}
);
}
boost::asio::io_context& ioc;
};
int main() {
boost::asio::io_context ioc;
auto sp_object = std::make_shared<object>(ioc);
sp_object->async_func(
[sp_object = std::move(sp_object)]
(int v) mutable { // mutable is for move
std::cout << v << std::endl;
sp_object->other_async_func(
[sp_object = std::move(sp_object)]
(int v) {
std::cout << v << std::endl;
}
);
}
);
std::vector<std::thread> ths;
ths.reserve(2);
for (std::size_t i = 0; i != 2; ++i) {
ths.emplace_back(
[&ioc] {
ioc.run();
}
);
}
for (auto& t : ths) t.join();
}
// The class of sp_object class doesn't use shared_from_this mechanism
auto sp_object = std::make_shared<object>(...);
auto& r = *sp_object;
r.async_func(
params,
[sp_object]
(boost::syste_error_code const&e, ...) {
if (e) return;
auto& r = *sp_object;
r.other_async_func(
params,
[sp_object]
(boost::syste_error_code const&e, ...) {
if (e) return;
// do some
}
);
}
);
// The class of sp_object class doesn't use shared_from_this mechanism
auto sp_object = std::make_shared<object>(...);
auto& r = *sp_object;
async_func(
r,
params,
[sp_object = std::move(sp_object)]
(boost::syste_error_code const&e, ...) mutable { // mutable is for move
if (e) return;
auto& r = *sp_object;
other_async_func(
r,
params,
[sp_object = std::move(sp_object)]
(boost::syste_error_code const&e, ...) {
if (e) return;
// do some
}
);
}
);
some_object.foo([bound_object = std::move(some_object)]() {
bound_object.bar()
});
auto callback = [bound_object = std::move(some_object)]() {
bound_object.bar()
}
some_object.foo(std::move(callback));