gpt4 book ai didi

c++ - 在 C++17 中调用对象生命周期之外的非静态成员函数

转载 作者:行者123 更新时间:2023-12-02 10:55:13 24 4
gpt4 key购买 nike

以下程序在 C++17 及更高版本中是否有未定义的行为?

struct A {
void f(int) { /* Assume there is no access to *this here */ }
};

int main() {
auto a = new A;
a->f((a->~A(), 0));
}

C++17 保证 a->f被评估为 A 的成员函数在评估调用的参数之前的对象。因此间接来自 ->是明确定义的。但是在进入函数调用之前,会评估参数并结束 A 的生命周期。对象(但请参阅下面的编辑)。调用是否仍有未定义的行为?是否可以通过这种方式调用对象生命周期之外的成员函数?
a->f的值(value)类别是 [expr.ref]/6.3.2 的纯右值和 [basic.life]/7只禁止非静态成员函数调用引用后生命周期对象的泛左值。这是否意味着调用有效? (编辑:正如评论中所讨论的,我可能误解了 [basic.life]/7,它可能确实适用于此。)

如果我替换析构函数调用 a->~A(),答案是否会改变?与 delete anew(a) A (与 #include<new> )?

关于我的问题的一些详细编辑和澄清:

如果我将成员函数调用和析构函数/删除/放置-new 分成两个语句,我认为答案很清楚:
  • a->A(); a->f(0) : UB,因为非静态成员调用a在其生命周期之外。 (不过,请参阅下面的编辑)
  • delete a; a->f(0) : 同上
  • new(a) A; a->f(0) : 定义良好,调用新对象

  • 然而,在所有这些情况下 a->f在第一个相应的语句之后排序,而在我最初的例子中这个顺序是相反的。我的问题是这种逆转是否允许答案发生变化?

    对于 C++17 之前的标准,我最初认为所有三种情况都会导致未定义行为,已经因为 a->f 的评估取决于 a 的值,但相对于对 a 产生副作用的参数的评估而言是无序的.但是,只有当标量值存在实际副作用时,这才是未定义的行为,例如写入标量对象。但是,没有写入标量对象,因为 A是微不足道的,因此我也对在 C++17 之前的标准的情况下究竟违反了什么约束感兴趣。特别是,placement-new 的情况现在对我来说似乎不清楚。

    我刚刚意识到关于对象生命周期的措辞在 C++17 和当前草案之间发生了变化。在 n4659(C++17 草案)[basic.life]/1 中说:

    The lifetime of an object o of type T ends when:

    • if T is a class type with a non-trivial destructor (15.4), the destructor call starts

    [...]



    current draft说:

    The lifetime of an object o of type T ends when:

    [...]

    • if T is a class type, the destructor call starts, or

    [...]



    因此,我想我的示例在 C++17 中确实有明确定义的行为,但在当前的 (C++20) 草案中没有,因为析构函数调用是微不足道的,而且 A 的生命周期很长。对象没有结束。我也希望对此进行澄清。对于用删除或放置新表达式替换析构函数调用的情况,我原来的问题仍然适用于 C++17。

    f访问 *this在它的主体中,那么对于析构函数调用和删除表达式的情况可能存在未定义的行为,但是在这个问题中,我想关注调用本身是否有效。
    但是请注意,我的问题与placement-new 的变化可能不会在 f 中出现成员访问问题。 ,取决于调用本身是否是未定义的行为。但在这种情况下,可能会有一个后续问题,特别是对于 Placement-new 的情况,因为我不清楚,是否 this在函数中将始终自动引用新对象或它是否可能需要潜在地是 std::launder ed(取决于成员 A 的成员)。

    虽然 A确实有一个微不足道的析构函数,更有趣的情况可能是它有一些副作用,编译器可能为了优化目的而做出假设。 (我不知道是否有任何编译器使用这样的东西。)因此,我欢迎对 A 的情况提供答案。也有一个重要的析构函数,特别是如果两种情况的答案不同。

    此外,从实际的角度来看,一个简单的析构函数调用可能不会影响生成的代码和(不太可能?)基于未定义行为假设的优化,所有代码示例很可能会生成在大多数编译器上按预期运行的代码。我对理论更感兴趣,而不是这种实际的观点。

    这个问题旨在更好地理解语言的细节。我不鼓励任何人写那样的代码。

    最佳答案

    确实,在(计划)C++20 之前,琐碎的析构函数根本不做任何事情,甚至不会结束对象的生命周期。所以问题是,呃,微不足道的,除非我们假设一个非微不足道的析构函数或像 delete 这样更强的东西。 .

    在这种情况下,C++17 的排序无济于事:调用(不是类成员访问)使用指向对象的指针 (to initialize this ),违反了 rules for out-of-lifetime pointers .

    旁注:如果只有一个订单未定义,那么 C++17 之前的“未指定订单”也是如此:if any of the possibilities对于未指定的行为是未定义的行为,该行为是未定义的。 (你怎么知道选择了明确定义的选项?未定义的选项可以模仿它,然后释放鼻恶魔。)

    关于c++ - 在 C++17 中调用对象生命周期之外的非静态成员函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58066136/

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