使用`std::forward_as_tuple`进行优化为什么会产生运行时错误?

huangapple go评论68阅读模式
英文:

Why does optimizing with std::forward_as_tuple produce runtime errors?

问题

Let's consider the following program:

#include <iostream>
#include <tuple>
using namespace std;

const int* f()
{
    static int i = 5;
    return &i;
}

int main()
{
    auto [a] = std::forward_as_tuple(f());
    //auto&& [a] = std::forward_as_tuple(f());           // same error
    //const auto& [a] = std::forward_as_tuple(f());      // same error
    cout << *a << endl;
    return 0;
}

我期望它输出 5。我在 gcc 13.1 和 MSVC 19.35 上尝试了它,并且在关闭所有优化时产生了正确的结果。但是,当我为 gcc 添加 -O1 或为 MSVC 添加 /O2 时,程序在运行时开始崩溃,我几乎无法理解为什么会发生这种情况。我在 cppreference 上找到了以下的 Note

如果参数是临时值,forward_as_tuple 不会延长它们的生命周期;它们必须在完整表达式结束之前使用。

这是解释吗?如果是的话,有人可以详细说明我在代码中违反了哪些语言规则吗?

英文:

Let's consider the following program:

#include &lt;iostream&gt;
#include &lt;tuple&gt;
using namespace std;

const int* f()
{
    static int i = 5;
    return &amp;i;
}

int main()
{
    auto [a] = std::forward_as_tuple( f() );
    //auto&amp;&amp; [a] = std::forward_as_tuple( f() );           // same error
    //const auto&amp; [a] = std::forward_as_tuple( f() );      // same error
    cout &lt;&lt; *a &lt;&lt; endl;
    return 0;
}

I expect it to print 5. I tried it on gcc 13.1 and MSVC 19.35, and it produces correct result when all optimizations are turned off. Anyway, when I add -O1 for gcc or /O2 for MSVC, the program starts crashing in runtime, and I can hardly understand why this happens. I found the following Note on cppreference:
> If the arguments are temporaries, forward_as_tuple does not extend their lifetime; they have to be used before the end of the full expression.

Is it the explanation? If so, can someone please elaborate what language rules am I violating in my code?

答案1

得分: 1

根据@NathanOliver提供的线索,我成功在cppreference找到了答案:

在函数调用中绑定到引用参数的临时对象存在于包含该函数调用的完整表达式的结束之前:如果该函数返回一个引用,该引用的生存期超过了完整表达式,那么它将变成悬空引用。

以及

一般而言,无法通过“传递它”来延长临时对象的生存期:“从绑定到临时对象的引用变量或数据成员初始化的第二个引用不会影响其生存期。

英文:

Following the trail that @NathanOliver had laid I managed to find an answer at cppreference:
> a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call: if the function returns a reference, which outlives the full expression, it becomes a dangling reference.

and
> In general, the lifetime of a temporary cannot be further extended by "passing it on": a second reference, initialized from the reference variable or data member to which the temporary was bound, does not affect its lifetime.

huangapple
  • 本文由 发表于 2023年5月26日 07:43:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76336845.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定