AnT*_*AnT 13 c++ scope lifetime destruction function-parameter
根据n4640中的5.2.2/4"函数调用"(n4659中的8.2.2/4),在调用者的上下文中创建和销毁函数参数.并且允许实现将函数参数的破坏延迟到封闭的完整表达式的末尾(作为实现定义的特征).请注意,选择不是未指定的,而是实现定义的.
(目前还不完全清楚这与3.3.3"块范围"(n4659中的6.3.3)是否一致,这似乎意味着函数参数具有块范围,然后是3.7.3"自动存储持续时间"(6.7.3)在n4659)中,它表示块作用域变量的存储一直存在,直到它们被创建的块出现.但是我们假设我在措辞中缺少/误解了一些内容.显然现在函数参数将有自己的范围)
据我所知,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;
}
Run Code Online (Sandbox Code Playgroud)
两个编译器都发出关于返回对局部变量的引用的警告,这在这里是完全合适的.快速检查生成的代码表明,两个编译器确实将参数的破坏延迟到表达式的末尾.但是,GCC仍故意返回"null引用" foo
,导致崩溃.与此同时,Clang表现为"按预期",返回对其参数的引用,该参数的s
存活时间足以产生预期的输出.
(简单来说,GCC在这种情况下很容易愚弄
std::string &foo(std::string s)
{
std::string *p = &s;
return *p;
}
Run Code Online (Sandbox Code Playgroud)
它修复了GCC下的段错误.)
在这种情况下GCC的行为是否合理,假设它保证参数的"晚期"破坏?我是否遗漏了标准中的其他段落,即返回对函数参数的引用始终未定义,即使它们的生命周期是由实现扩展的?
据我所知,ABI要求GCC和Clang将函数参数的破坏延迟到完整表达式的末尾
这个问题在很大程度上依赖于这个假设.让我们看看它是否正确.Itanium C++ ABI草案3.1.1值参数说
如果类型具有非平凡的析构函数,则在封闭full-expression结束时,调用者在控制返回之后调用该析构函数(包括调用者抛出异常时).
ABI没有定义生命周期,所以让我们检查一下C++标准草案N4659 [basic.life]
1.2 ...类型T的对象o的生命周期结束时:
1.3如果T是具有非平凡析构函数(15.4)的类类型,则析构函数调用开始,或者......
1.4对象占用的存储被释放,或被未嵌套在o中的对象重用([intro.object]).
C++标准表示,在调用析构函数时,生命周期结束.因此,ABI确实要求函数参数的生命周期扩展函数调用的完整表达式.
假设实现定义了需求,我在示例程序中看不到UB,因此它应该在任何保证遵循Itanium C++ ABI的实现上具有预期的行为.海湾合作委员会似乎违反了这一点.
GCC文档确实说明了这一点
从GCC版本3开始,GNU C++编译器使用行业标准的C++ ABI,即Itanium C++ ABI.
因此,证明的行为可能被视为一个错误.
另一方面,不清楚[expr.call]改变措辞的后果是否是故意的.结果可能被认为是一个缺陷.
...表示块作用域变量的存储一直持续到创建它们的块为止.
确实.但是你引用的[expr.call]/4表示"函数参数是在调用者的上下文中创建和销毁的 ".因此,存储一直持续到函数调用块的结尾.似乎与存储持续时间没有冲突.
请注意,C++标准链接指向从当前草稿定期生成的站点,因此可能与我引用的N4659不同.
归档时间: |
|
查看次数: |
387 次 |
最近记录: |