【发布时间】:2018-07-05 08:28:08
【问题描述】:
根据 5.2.2/4 “函数调用” in n4640(8.2.2/4 in n4659)函数参数在调用者的上下文中创建和销毁。并且允许实现将函数参数的破坏延迟到封闭的完整表达式的末尾(作为实现定义的功能)。请注意,选择不是未指定,而是实现定义。
(不完全清楚这与3.3.3“块范围”(n4659中的6.3.3)是如何一致的,这似乎暗示函数参数具有块范围,然后是3.7.3“自动存储”持续时间”(n4659 中的 6.7.3),它表示块范围变量的存储将持续到创建它们的块退出。但是让我们假设我在措辞中遗漏/误解了某些内容。 显然现在函数参数将有their own scope)
据我所知,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 仍然故意从foo 返回一个“空引用”,从而导致崩溃。同时,Clang 的行为“如预期”,返回对其参数 s 的引用,该参数的生存时间足以产生预期的输出。
(在这种情况下,GCC 很容易被愚弄
std::string &foo(std::string s)
{
std::string *p = &s;
return *p;
}
它修复了 GCC 下的段错误。)
在这种情况下,GCC 的行为是否合理,假设它保证“后期”破坏参数?我是否遗漏了标准中的其他段落,即返回对函数参数的引用始终未定义,即使实现延长了它们的生命周期?
【问题讨论】:
-
我不是语言律师,虽然我在电视和工作中玩过。
"Hello world"是一个参数;s是一个参数。参数s的块作用域在foo最外层块作用域的返回处结束。 -
@Eljay:您的术语是正确的。但是 5.2.2 专门讨论了 parameters。文中相应的变化是由 DR#1880 (open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1880) 引起的,其中也专门谈到了parameters。 arguments 甚至不会出现这个问题。
-
“5.2.2/4”我猜你的意思是 N4659 中的 8.2.2/4 (expr.call/4)
-
您能说明这个问题适用的标准版本吗?我假设是 C++17,但将来不会很明显。
-
我猜 gcc 在返回本地/参数作为覆盖滥用的一揽子措施时返回空引用。
标签: c++ scope lifetime function-parameter destruction