C++的寿命是多少方法指针表达式?
我有一个通用函数,可以调用它的可可。与std :: Invoke
的呼叫的道德等同,除了它是Coroutine的一部分:
// Wait for some condition to be true, then invoke f with the supplied
// arguments. The caller must ensure that all references remain valid
// until the returned task is done.
template <typename F, typename Args...>
Task<void> WaitForConditionAndInvoke(F&& f, Args&&... args) {
co_await SomeCondition();
std::invoke(f, std::forward<Args>(args)...);
}
由于要求输入参数保持有效,因此通过WaitforCondition and Incoke 诸如lambda之类的临时性(除非返回的任务是直接
co_await
作为单个表达式的一部分)。即使使用Unary +
将具有绑定状态的lambda转换为函数指针的lambda也是如此:功能指针本身是暂时的,Asan似乎在被销毁后正确地抱怨使用它。
我的问题是使用会员函数指针是否合法:
struct Object {
void SomeMethod();
};
// Some global object; we're not worried about the object's lifetime.
Object object;
Task<void> WaitForConditionAndInvokeOnGlobalObject() {
return WaitForConditionAndInvoke(&Object::SomeMethod, &object);
}
这似乎可以正常工作,但是我不清楚指针的寿命'object :: somemethod
评估IS。这是否可以保证是恒定的表达,即不是暂时的?标准的哪一部分涵盖了这一点?
I have a generic function that invokes a callable it's handed; the moral equivalent of a call to std::invoke
, except it's part of a coroutine:
// Wait for some condition to be true, then invoke f with the supplied
// arguments. The caller must ensure that all references remain valid
// until the returned task is done.
template <typename F, typename Args...>
Task<void> WaitForConditionAndInvoke(F&& f, Args&&... args) {
co_await SomeCondition();
std::invoke(f, std::forward<Args>(args)...);
}
Because of the requirement that the input arguments remain valid, it's not always legal to pass WaitForConditionAndInvoke
a temporary like a lambda (unless e.g. the returned task is directly co_await
ed as part of a single expression). This is true even if using unary +
to convert a lambda with no bound state to a function pointer: the function pointer is itself a temporary, and asan seems to correctly complain about using it after it's destroyed.
My question is whether using a member function pointer is legal:
struct Object {
void SomeMethod();
};
// Some global object; we're not worried about the object's lifetime.
Object object;
Task<void> WaitForConditionAndInvokeOnGlobalObject() {
return WaitForConditionAndInvoke(&Object::SomeMethod, &object);
}
This seems to work fine, but it's unclear to me what the lifetime of the pointer that &Object::SomeMethod
evaluates to is. Is this guaranteed to be a constant expression, i.e. not a temporary? What part of the standard covers this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
waitforcondition和Invoke
coroutine将是危险的,除非每个参数在内,包括函数f
是指拥有寿命足够长的对象。例如,waitforconditionandInvoke(std :: abs,1)
具有未定义的行为,因为该对象构成了用int> int
prvalue expression1 。尽管恒定的表达值可以帮助编译器以使用死对象的已知值“工作”的方式来实现编译器,但在此处没有差异。
为了解决此问题,您可以使您的函数将每个RVALUE参数移动到本地对象中:
或更安全,以
std :: bind
执行操作,然后移动/复制所有内容。调用代码可以指定使用std :: ref
或std :: cref
的函数或函数参数作为参考而无需移动或复制。实际上,该实施仅是:That
WaitForConditionAndInvoke
coroutine will be dangerous unless every argument including the functorf
refers to an object with lifetime long enough. For example,WaitForConditionAndInvoke(std::abs, 1)
has undefined behavior because of the object materialized to initialize a reference with theint
prvalue expression1
. There is no difference per the Standard for constant expression arguments here, although a constant expression value could help compilers implement it in a way which "works" using a dead object's known value.To fix this, you could have your function move every rvalue argument into a local object:
Or to be even safer, do as
std::bind
does, and move/copy everything. The calling code can specify that the functor or functor argument(s) should be treated as a reference with no move or copy usingstd::ref
orstd::cref
. In fact, that implementation is just:正如评论者所说,这里不存在终身问题。
&Object::SomeMethod
是一个常量表达式,就像42
或&WaitForConditionAndInvokeOnGlobalObject
一样。您可能会因为将类型命名为
struct Object
而感到困惑,因此表达式&Object::SomeMethod
看起来看起来就像涉及一个对象...但事实并非如此;Object
是类型的名称。该表达式中不涉及任何对象;它是一个简单的编译时常量,就像offsetof(Object, SomeDataMember)
或sizeof(Object)
一样。不涉及对象=不涉及生命周期问题。
编辑(从而完全改变我的答案):啊,aschepler 是对的,你关心的是
F&& 所引用的事物的生命周期。 f
其中是值为
&Object::SomeMethod
的临时对象,它当然在其完整表达式的末尾处终止,即在 < 末尾的分号处终止代码>返回WaitForConditionAndInvoke(&Object::SomeMethod, &object);。一旦您点击该分号,所有临时变量都会超出范围,并且对它们的任何引用(例如,在协程框架中捕获的f
)肯定会悬空。As the commenters say, there's no lifetime issue here.
&Object::SomeMethod
is a constant expression, just like42
or&WaitForConditionAndInvokeOnGlobalObject
.You might have confused yourself by naming your type
struct Object
, so the expression&Object::SomeMethod
kind of looks like it involves an object... but it doesn't;Object
is the name of a type. There is no object involved in that expression; it's a simple compile-time constant, just likeoffsetof(Object, SomeDataMember)
orsizeof(Object)
would be.No object involved = no lifetime issues involved.
EDIT (thus completely changing my answer): Ah, aschepler is right, you're concerned with the lifetime of the thing-referred-to-by-
F&& f
inwhich is the temporary object with value
&Object::SomeMethod
, which of course dies at the end of its full-expression, i.e., at the semicolon at the end ofreturn WaitForConditionAndInvoke(&Object::SomeMethod, &object);
. As soon as you hit that semicolon, all of the temporaries go out of scope and any references to them (like, thatf
captured in the coroutine frame) are definitely dangling.