删除自身内部的std :: function对象 [英] Deleting a std::function object within itself

查看:259
本文介绍了删除自身内部的std :: function对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这种行为是否明确定义?

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) where obj 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:


  • 内建的一元操作符* 取消引用(在prvalue this

  • 成员访问表达式

  • 成员子对象的值计算(即左值到右值转换)

  • 标量删除表达式

    • 调用 std :: function< ; void()> ::〜function()

    • 调用 void运算符delete(void *)

    • The built-in unary operator* dereference (on the prvalue this)
    • 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆