为啥在这个例子中可以返回临时对象?

Posted

技术标签:

【中文标题】为啥在这个例子中可以返回临时对象?【英文标题】:Why the temporary object can be returned in this example?为什么在这个例子中可以返回临时对象? 【发布时间】:2018-03-30 08:18:55 【问题描述】:

char*[] 对象可以通过 const 引用传递给函数 version2() 和 version 3(),这意味着 s2 是一个临时对象,等于“###”或“@@@”。

但是为什么临时对象 s2可以返回,而临时对象temp不能。

int main()

    result = version2(input, "###");
    cout << "Your string enhanced: " << result << endl;

    result = version3(input, "@@@");
    cout << "Your string enhanced: " << result << endl;
    return 0;


// No Error
const string & version2(string & s1, const string & s2)

    return s2; 


// Error
const string & version3(string & s1, const string & s2)

    string temp = s2 + s1 + s2;
    return temp;

【问题讨论】:

【参考方案1】:

重点是object life time。

对于第一种情况,如您所说,创建一个临时的std::string 并将其传递给version2(绑定到参数s2)。临时将在完整表达式后被销毁,即整个result = version2(input, "###");。这意味着version2 返回的引用仍然有效,可以将其用于分配给result

所有临时对象都在评估完整表达式的最后一步被销毁,该完整表达式(词法上)包含它们的创建点,

对于第二种情况,在version3内部创建了一个本地对象temp,当离开version3时它将被销毁。这意味着version3 将始终返回一个悬空引用。并使用它分配给result 导致UB。编译器可能会给出诊断;但不必这样做。

【讨论】:

谢谢。但是对于第一种情况,谁实际创建临时对象并决定何时销毁它。我是 C++ 新手,我正在学习的书(C++ 入门加)对此几乎没有提及。那么你能推荐一些书籍或博客吗?谢谢。 在第一种情况下,临时是作为函数调用的一部分在函数体开始之前创建并在函数体结束后销毁;这基本上就是您需要知道的所有内容(对于其他所有内容,您可以检查程序集)。另外,我们这里有一个关于书籍的线程,所以你可以搜索它。 @LeoHu 我建议使用 cppreference.com(正如我在回答中链接的那样),您可以从 The Definitive C++ Book Guide and List 找到一本好书。【参考方案2】:

两个版本都返回对对象的引用。

version2 从 main 中获得了一个引用,并将其原封不动地返回。

version3 定义了一个局部变量temp 并希望返回对该局部变量的引用。但不幸的是,当您从version3 返回时,本地不再存在。因此,您将返回一个已经被销毁的引用。

【讨论】:

【参考方案3】:

我之所以这么说是因为编译器认为version2 并不危险。 请注意,对于version3,它实际上给出的是警告,而不是错误(除非你告诉它)。

其实如果你改成catch result by reference,还是不会有警告的,但是这个临时的无疑已经被销毁了。您可以检查组件(或标准),但在这里我复制它并使其易于观察。 (尽管这取决于编译器)

wandbox example

如果您切换优化,您可以看到输出更改并且不会发出任何警告。


以防万一,这里是测试代码

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


// No Error
auto& f(const string & s)return s;

int main()

    char s[]="#";
    const char* cs = s;
    auto& x = f(cs);
    s[0]='o';
    [[maybe_unused]] auto& y = f(cs);
    cout << x;

g++7.2 -Wall -Wextra -std=c++17

【讨论】:

以上是关于为啥在这个例子中可以返回临时对象?的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个 SQL 存储过程需要创建一个临时表才能工作(返回结果)?

为啥非常量引用不能绑定到临时对象?

为啥我们在 NSManagedObject 中需要临时 ID

为啥我可以在临时 std::ofstream 对象上使用 `operator<<`?

自制反汇编工具使用实例 其一

为啥我们可以非常量引用临时对象并延长其生命周期?