删除自身内部的std :: function对象 [英] Deleting a std::function object within itself
问题描述
这种行为是否明确定义?
Is this well defined behavior?
#include <functional>
void foo() {
auto f = new std::function<void()>;
*f = [f]() { delete f; };
(*f)();
f = nullptr;
}
int main() {
foo();
}
使用最新的g ++,如果我在模板中执行此操作,则会导致无效在valgrind下运行时读取,否则可以正常工作。为什么?这是g ++中的错误吗?
Using the most recent g++, if I do this within a template it causes invalid reads while running under valgrind, otherwise it works fine. Why? Is this a bug in g++?
#include <functional>
template<std::size_t>
void foo() {
auto f = new std::function<void()>;
*f = [f]() { delete f; };
(*f)();
f = nullptr;
}
int main() {
foo<0>();
}
推荐答案
该程序定义明确
运行时唯一可疑的部分是在语句(* f)();
期间。该行的行为可以逐个分开。下面的标准部分编号是从N3485起的;抱歉,如果某些代码与C ++ 11不匹配。
The only questionable part of runtime is during the statement (*f)();
. The behavior of that line can be picked apart piece by piece. The Standard section numbers below are from N3485; apologies if some don't match C++11.
* f
只是原始指针上的内置一元运算符上课类型。没问题唯一的其他评估是函数调用表达式(* f)()
,该表达式调用 void std :: function< void()> ;: :operator()const
。那么该全表达式就是一个废弃的值。
*f
is just the built-in unary operator on a raw pointer to class type. No problem here. The only other evaluation is the function-call expression (*f)()
, which invokes void std::function<void()>::operator() const
. Then that full-expression is a discarded value.
20.8.11.2.4:
20.8.11.2.4:
R operator()(ArgTypes... args) const
效果:
调用
(obj,std :: forward< ArgTypes>(args)...,R)
其中obj
是* this
的目标对象。
Effects:
INVOKE
(obj, std::forward<ArgTypes>(args)..., R)
whereobj
is the target object of*this
.
(我已将标准中的 f
替换为 obj
,以减少与 main
的 f
。)
(I've replaced "f
" in the Standard with "obj
" to reduce confusion with main
's f
.)
此处 obj
是lambda对象的副本, ArgTypes
是专业化 std :: function< void的空参数包()>
,而 R
是 void
。
Here obj
is a copy of the lambda object, ArgTypes
is the empty parameter pack from the specialization std::function<void()>
, and R
is void
.
INVOKE
伪宏在20.8.2中定义。由于 obj
的类型不是指向成员的指针,因此 INVOKE
(obj,void)
被定义为 obj()
隐式转换为 void
。
The INVOKE
pseudo-macro is defined in 20.8.2. Since the type of obj
is not a pointer-to-member, INVOKE
(obj, void)
is defined to be obj()
implicitly converted to void
.
5.1.2p5:
lambda表达式的闭包类型具有公共的
内联
函数调用运算符...
The closure type for a lambda-expression has a public
inline
function call operator ...
...宣言。在这种情况下,结果是 void operator()const
。并且它的定义也得到了准确描述:
... with exactly described declaration. In this case it turns out to be void operator() const
. And its definition is exactly described too:
5.1.2p7:
lambda表达式的 compound-statement 产生函数调用操作符的 function-body ,但出于名称查找的目的,确定
this
并使用(* this)
id-expressions 转换为类成员访问表达式>,在 lambda-expression 上下文中考虑 compound-statement 。
The lambda-expression's compound-statement yields the function-body of the function call operator, but for purposes of name lookup, determining the type and value of
this
and transforming id-expressions referring to non-static class members into class member access expressions using(*this)
, the compound-statement is considered in the context of the lambda-expression.
5.1.2p14:
对于每个通过副本捕获的实体,在闭包类型中声明了一个未命名的非静态数据成员。
For each entity captured by copy, an unnamed non-static data member is declared in the closure type.
5.1.2p17:
每个 id-expression 都是odr-通过复制捕获的实体的使用将转换为对闭包类型的相应未命名数据成员的访问。
Every id-expression that is an odr-use of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type.
因此, mbda函数调用运算符必须等效于:
So the lambda function call operator must be equivalent to:
void __lambda_type::operator() const {
delete __unnamed_member_f;
}
(我为未命名的lambda类型和未命名的数据成员发明了一些名称。)
(where I've invented some names for the unnamed lambda type and unnamed data member.)
该调用运算符的单个语句当然等效于 delete(* this).__ unnamed_member_f;
因此,我们有:
The single statement of that call operator is of course equivalent to delete (*this).__unnamed_member_f;
So we have:
- 内建的一元
操作符*
取消引用(在prvaluethis
) - 成员访问表达式
- 成员子对象的值计算(即左值到右值转换)
- 标量
删除
表达式
- 调用
std :: function< ; void()> ::〜function()
- 调用
void运算符delete(void *)
- The built-in unary
operator*
dereference (on the prvaluethis
) - A member access expression
- A value computation (aka lvalue-to-rvalue conversion) for the member subobject
- A scalar
delete
expression- Invokes
std::function<void()>::~function()
- Invokes
void operator delete(void*)
最后,在5.3.5p4中:
And finally, in 5.3.5p4:
delete-expression 中的 cast-expression 应该被精确评估一次。
The cast-expression in a delete-expression shall be evaluated exactly once.
(这是g ++错误的地方,对t进行第二次值计算
(Here is where g++ is wrong, doing a second value computation on the member subobject between the destructor call and the deallocation function.)
在
删除
This code cannot cause any other value computations or side effects after the
delete
expression.在lambda类型和lambda对象中对实现定义的行为有一些允许,但没有任何影响上面的内容:
There are some allowances for implementation-defined behavior in lambda types and lambda objects, but none that affect anything above:
5.1。 2p3:
实现可以定义闭包类型,其方式与以下描述不同,但前提是除了更改之外,不改变程序的可观察行为:
An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:
-
闭合类型的大小和/或对齐方式,
the size and/or alignment of the closure type,
闭包类型是否可以普通复制,
whether the closure type is trivially copyable,
闭包类型是否为标准布局类,或者
whether the closure type is a standard-layout class, or
关闭类型是否为POD类。
whether the closure type is a POD class.
这篇关于删除自身内部的std :: function对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- Invokes
- 调用