gpt4 book ai didi

c++ - 删除自身内的 std::function 对象

转载 作者:可可西里 更新时间:2023-11-01 15:05:30 24 4
gpt4 key购买 nike

这是明确定义的行为吗?

#include <functional>

void foo() {
auto f = new std::function<void()>;
*f = [f]() { delete f; };
(*f)();
f = nullptr;
}

int main() {
foo();
}

使用最新的 g++,如果我在模板中执行此操作,则在 valgrind 下运行时会导致无效读取,否则它会正常工作。为什么?这是 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>();
}

最佳答案

此程序具有明确定义的行为并演示了 g++ 错误。

运行时唯一有问题的部分是在语句 (*f)(); 期间.该行的行为可以一段一段地分开。以下标准部分编号来自 N3485;如果有些与 C++11 不匹配,我们深表歉意。

*f只是指向类类型的原始指针的内置一元运算符。这里没问题。唯一的其他评估是函数调用表达式 (*f)() ,调用 void std::function<void()>::operator() const .那么那个完整的表达式就是一个丢弃的值。

20.8.11.2.4:

R operator()(ArgTypes... args) const

Effects: INVOKE(obj, std::forward<ArgTypes>(args)..., R) where obj is the target object of *this.

(我已将标准中的“f”替换为“obj”,以减少与 mainf 的混淆。)

在这里obj是 lambda 对象的拷贝,ArgTypes是来自特化的空参数包 std::function<void()> , 和 Rvoid .

INVOKE 伪宏在 20.8.2 中定义。自类型 obj不是指向成员的指针, INVOKE (obj, void)定义为 obj()隐式转换为 void .

5.1.2p5:

The closure type for a lambda-expression has a public inline function call operator ...

... 带有准确描述的声明。在这种情况下,结果是 void operator() const .并且它的定义也被准确描述:

5.1.2p7:

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:

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.

因此 lambda 函数调用运算符必须等效于:

void __lambda_type::operator() const {
delete __unnamed_member_f;
}

(我为未命名的 lambda 类型和未命名的数据成员发明了一些名称。)

该调用运算符的单个语句当然等同于 delete (*this).__unnamed_member_f;所以我们有:

  • 内置一元 operator*取消引用(在纯右值 this 上)
  • 成员访问表达式
  • 成员子对象的值计算(又名左值到右值转换)
  • 一个标量 delete表达
    • 调用 std::function<void()>::~function()
    • 调用 void operator delete(void*)

最后,在 5.3.5p4 中:

The cast-expression in a delete-expression shall be evaluated exactly once.

(这里是 g++ 错误的地方,在析构函数调用和释放函数之间对成员子对象进行第二次值计算。)

此代码不会在 delete 之后导致任何其他值计算或副作用表达。

在 lambda 类型和 lambda 对象中允许实现定义的行为,但不会影响以上任何内容:

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

  • whether the closure type is a POD class.

关于c++ - 删除自身内的 std::function 对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22998364/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com