wal*_*nut 28 c++ language-lawyer order-of-execution c++17 c++20
以下程序在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));
}
Run Code Online (Sandbox Code Playgroud)
C ++ 17保证a->f在评估A调用的参数之前先对对象的成员函数进行评估。因此,来自的间接->定义是明确的。但是在输入函数调用之前,将评估参数并结束A对象的生存期(但是请参见下面的编辑内容)。通话是否仍存在未定义的行为?是否可以通过这种方式在对象的生存期之外调用它的成员函数?
[expr.ref] /6.3.2的值类别a->f为prvalue ,[basic.life] / 7仅禁止对引用了生存期对象的glvalue进行非静态成员函数调用。这是否表示通话有效?(编辑:如评论中所讨论,我可能会误会[basic.life] / 7,它可能确实适用于此。)
如果我a->~A()用delete a或new(a) A(用#include<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之前的标准情况下完全违反什么约束感兴趣。特别是,现在对我来说不清楚新安置的情况。
我刚刚意识到,有关对象生存期的措辞在C ++ 17和当前草案之间发生了变化。在n4659(C ++ 17草案)中,[basic.life] / 1说:
T类型的对象o的生存期在以下情况下结束:
- 如果T是具有非平凡析构函数(15.4)的类类型,则析构函数调用开始
[...]
而目前的草案说:
T类型的对象o的生存期在以下情况下结束:
[...]
- 如果T是一个类类型,则析构函数调用开始,或者
[...]
因此,我想我的示例在C ++ 17中确实具有明确定义的行为,但在当前(C ++ 20)草案中却没有,因为析构函数调用是微不足道的,并且A对象的生存期没有结束。我也希望对此进行澄清。对于用delete或placement-new表达式替换析构函数调用的情况,我的原始问题甚至对于C ++ 17仍然有效。
如果在其主体中进行f访问*this,则在析构函数调用和删除表达式的情况下可能存在未定义的行为,但是在此问题中,我想集中讨论调用本身是否有效。但是请注意f,根据调用本身是否是未定义的行为,我的问题使用new-place的变化可能不会对中的成员访问产生影响。但是在那种情况下,可能会有一个后续问题,特别是对于新放置的情况,因为我不清楚,this函数中是否会始终自动引用新对象,或者是否可能需要对其进行std::launder编辑(取决于成员的A身份)。
尽管A确实有一个琐碎的析构函数,但更有趣的情况可能是它具有一些副作用,编译器可能会出于优化目的而对其进行假设。(我不知道是否有任何编译器使用这样的东西。)因此,对于A具有非平凡析构函数的情况,我也欢迎给出答案,尤其是在两种情况下答案不同的情况下。
同样,从实际角度看,琐碎的析构函数调用可能不会影响生成的代码,并且(不太可能?)基于未定义行为假设的优化,所有代码示例极有可能生成可在大多数编译器上按预期运行的代码。我对理论而非实际观点更感兴趣。
这个问题旨在更好地理解语言的细节。我不鼓励任何人编写这样的代码。
后缀表达式a->f是之前测序的任何参数(其不定相对于彼此排序)的评估。(请参阅[expr.call])
参数的评估在函数主体之前排序(甚至是内联函数,请参见[intro.execution])
这意味着,调用函数本身不是未定义的行为。但是,访问每个成员变量或调用其中的其他成员函数将是每个[basic.life]的UB。
因此,得出的结论是,根据措辞,此特定实例是安全的,但通常来说是一种危险的技术。