C++ 函数参数的后期销毁

C++ 函数参数的后期销毁,c++,scope,lifetime,function-parameter,destruction,C++,Scope,Lifetime,Function Parameter,Destruction,根据n4640(n4659)中的5.2.2/4“函数调用”,函数参数在调用方的上下文中创建和销毁。并且允许实现将函数参数的销毁延迟到封闭的完整表达式的末尾(作为实现定义的特性)。请注意,选择不是未指定的,而是定义了实现 (不完全清楚这与3.3.3“块范围”(n4659中的6.3.3)如何一致,这似乎意味着功能参数具有块范围,然后是3.7.3“自动存储持续时间”(n4659中的6.7.3),表示块作用域变量的存储将一直持续到创建它们的块退出为止。但让我们假设我在措辞中遗漏了/误解了某些内容。显然,

根据n4640(n4659)中的5.2.2/4“函数调用”,函数参数在调用方的上下文中创建和销毁。并且允许实现将函数参数的销毁延迟到封闭的完整表达式的末尾(作为实现定义的特性)。请注意,选择不是未指定的,而是定义了实现

(不完全清楚这与3.3.3“块范围”(n4659中的6.3.3)如何一致,这似乎意味着功能参数具有块范围,然后是3.7.3“自动存储持续时间”(n4659中的6.7.3),表示块作用域变量的存储将一直持续到创建它们的块退出为止。但让我们假设我在措辞中遗漏了/误解了某些内容。显然,现在函数参数将有)

据我所知,ABI要求GCC和Clang将函数参数的销毁延迟到完整表达式的末尾,也就是说,这是这些编译器的实现定义行为。我想在这样的实现中,只要函数参数的引用/指针仅在调用表达式中使用,就可以返回这些引用/指针

但是,下面的示例在GCC中出现故障,在Clang中工作良好

#include <iostream>
#include <string>

std::string &foo(std::string s)
{
  return s;
}

int main()
{
   std::cout << foo("Hello World!") << std::endl;
}
它修复了GCC下的segfault。)


在这种情况下,GCC的行为是否合理,前提是它保证了参数的“延迟”销毁?我是否遗漏了标准中的另一段话,即返回函数参数的引用总是未定义的,即使它们的生命周期通过实现而延长?

来自5.2.2/4函数调用[expr.call],在我看来GCC是正确的:

参数的生存期在其所在的函数终止时结束 定义的回报。每个参数的初始化和销毁 在调用函数的上下文中发生


好吧,我的坏消息是,我给出了前C++14标准的以下答案,阅读了C++17,在我看来,GCC和Clang都是正确的:

From:N4659 8.2.2/4函数调用[expr.call]

参数的生存期是否结束由实现定义 当定义它的函数返回或在 包含完整表达式。初始化和销毁每个 参数发生在调用函数的上下文中

据我所知,ABI要求GCC和Clang将函数参数的销毁延迟到完整表达式的末尾

这个问题在很大程度上依赖于这一假设。让我们看看它是否正确。IATA+ C++ ABI草案表示

如果类型有一个非平凡的析构函数,则调用方将在控件返回该析构函数后(包括调用方抛出异常时)在封闭完整表达式的末尾调用该析构函数

没有定义生命周期,所以让我们检查C++标准草案N469

1.2。。。类型为T的对象o的生存期在以下情况下结束:

1.3如果T是具有非平凡析构函数(15.4)的类类型,则析构函数调用将启动,或

1.4对象占用的存储被释放,或被未嵌套在o([intro.object])中的对象重用

C++标准称,在调用析构函数时,生命周期结束。因此,ABI确实要求函数参数的生存期扩展函数调用的完整表达式

假设实现定义的需求,我在示例程序中没有看到UB,因此它应该在任何实现遵循ItAuthC++ C++ ABI的预期行为上。GCC似乎违反了这一点

请说明

<>从GCC 3版开始,GNU C++编译器使用行业标准C++ ABI、ItAuthC++ C++ ABI。 因此,所演示的行为可以被视为bug

另一方面,尚不清楚[expr.call]措词改变的这一结果是否是故意的。结果可能被认为是缺陷


。。。也就是说,块作用域变量的存储一直持续到创建它们的块退出为止

的确如此。但是您引用的/4表示“函数参数是在调用方的上下文中创建的和销毁的”。因此,存储将持续到函数调用的块结束。似乎与存储持续时间没有冲突



<>请注意,C++标准链接指向一个站点,该站点是从当前草稿中周期性生成的,因此可能与我引用的N469不同。

我不是语言律师,虽然我在电视和工作中玩过一个。
“Hello world”
是一个参数<代码>s是一个参数。参数
s
的块范围在
foo
最外层块范围的返回处结束。@Eljay:您的术语是正确的。但5.2.2专门讨论了参数。文本中相应的变化是由DR#1880()引起的,它还专门讨论了参数。这个问题甚至不会在争论中出现。我想你说的“5.2.2/4”是指N4659(expr.call/4)中的8.2.2/4。你能说明这个问题适用的标准版本吗?我假设是C++17,但这在将来不会很明显。我猜gcc在返回局部/参数时会返回一个空引用,作为覆盖误用的总体措施。您引用的是哪个版本的标准?此外,请提及章节名称或括号内的缩写,因为不同版本的编号不同。N3690,请参阅,5.2.2/4.N3690文本中的相应更改是C++14之前的草稿。我们已经超过C++17了now@user167921据我所知,N4659与C++17相同,而N4700是后C++17草案。所以我建议使用N4659@user167921在这种情况下,GCC的行为是否合理,前提是它保证“延迟”de
std::string &foo(std::string s)
{
  std::string *p = &s;
  return *p;
}