【问题标题】:Lifetime extension, prvalues and xvalues寿命延长、prvalues 和 xvalues
【发布时间】:2017-07-15 11:19:02
【问题描述】:

按照这个问题的公认答案Do rvalue references allow dangling references? 看起来,当分配给问题中的右值引用左值时,xvalues 的生命周期并没有延长。但是当我这样做时

#include <iostream>

using namespace std;

class Something {
public:
    Something() {
        cout << "Something()" << endl;
    }
    Something(const Something&) {
        cout << "Something(const Something&)" << endl;
    }
    Something(Something&&) {
        cout << "Something(Something&&)" << endl;
    }
    ~Something() {
        cout << "~Something()" << endl;
    }

    int a;
};

Something make_something() {
    return Something{};
}

int main() {
    auto&& something = make_something().a;

    return 0;
}

通过调用make_something 返回的对象的生命周期被延长,即使make_something().a 是根据http://en.cppreference.com/w/cpp/language/value_category 的xvalue(xvalues 解释中的第三个项目符号列出了我在上面作为xvalue 拥有的成员访问权限,)

a.m,对象表达式的成员,其中 a 是右值,m 是 a 非引用类型的非静态数据成员;

如果值类别不能确定右值的生命周期何时会延长,那该怎么办?当右值的生命周期在 C++ 中延长时,我很难理解

【问题讨论】:

    标签: c++ c++11 xvalue prvalue value-categories


    【解决方案1】:

    生命周期延长不关心价值类别。正如 [class.temporary]/p6 所述:

    引用绑定到的临时对象或作为引用绑定到的子对象的完整对象的临时对象在引用的生命周期内持续存在

    添加了重点。

    这里没有说明被引用表达式的值类别。

    决定一个临时对象是否被扩展的正是上述(以及更多规则)。


    但这并不能解释为什么在分配引用的临时对象周围添加 std::move() 不会延长生命周期

    std::move 不是 C++ 中由编译器定义的神奇构造。它是一个函数调用,因此它的行为与任何其他 C++ 函数调用没有什么不同。

    那么,如果你有std::move(Type()),那是什么意思?这意味着您将创建一个临时的,将其绑定到std::move参数,然后调用该函数,该函数将返回一些内容。

    将临时对象绑定到函数参数,如 [class.temporary]/p6 中所述,意味着临时对象的生命周期固定为创建它的完整表达式的生命周期(如果不是该规则,则临时对象必须在函数调用结束时被销毁,因为那是引用生命周期的结束)。

    函数做什么、说什么或暗示什么并不重要。编译器是否可以内联并确定返回值是对来自临时参数的引用并不重要。该临时的生命周期是固定的表达式,而不是延长。

    【讨论】:

    • 我更新了我的问题以在最后包含另一部分。
    • 但这并不能解释为什么在分配引用的临时对象周围添加std::move() 不会延长生命周期
    • @Curious 嗯...为什么不呢?你有没有想过“在临时文件周围添加一个 std::move()”实际上是什么意味着
    • 也许还不够,我只是认为它将表达式转换为可移动的 xvalue,因此可以绑定到右值引用。我在这里错过了什么吗?
    • @Curious: "std::move() 返回的值的生命周期没有延长。" 值的生命周期没有延长;值根本没有生命周期。对象有生命周期,临时对象可以延长它们的生命周期。 move 的返回值是一个引用,而不是一个对象。
    【解决方案2】:

    如果值类别不能确定右值的生命周期何时会延长,那该怎么办?当右值的生命周期在 C++ 中延长时,我很难理解

    请注意,值类别描述的是表达式而不是对象。值类别(xvalue、prvalue 或其他)不会以任何方式扩展。只有对象可以有生命周期。

    来自 n4296 标准草案:

    • §12.2.1

      类类型的临时对象在各种上下文中创建:将引用绑定到纯右值(8.5.3),返回 prvalue (6.6.3),创建 prvalue (4.1, 5.2.9, 5.2.11, 5.4) 的转换,抛出异常 (15.1), 并在一些初始化 (8.5) 中。

    • §12.2.4

      在两种情况下,临时对象在与完整结束时不同的点被销毁- 表达。 [...]
      第二个上下文是当一个引用被绑定到一个临时的。引用的临时对象 bound 或 temporary 是引用所绑定的子对象的完整对象 在参考的生命周期内

    注意:我没有引用第一个上下文,因为它对这些问题有轻微的启示。我添加的斜体强调。

    因此,根据上面引用的第一段,函数 表达式 makesomething()值类别 是一个 prvalue 创建类类型的临时对象。
    makesomething().a 访问一个临时的、完整的子对象。根据上面引用的第二个上下文,将此临时绑定到引用会导致更长的生命周期。


    子对象a 的生命周期与先前创建的临时对象的生命周期耦合,使其成为到期值(xvalue)。如果不通过将其绑定到引用来延长其生命周期,它将与临时类对象一起被销毁。因此,在这种情况下,在; 之后。

    【讨论】:

    • 您能否粘贴第 12.2.4 节中提到的第一个上下文?
    • @Curious 这实际上只是一种不太重要的语言微妙之处。有兴趣的可以在标准中查找。将其添加到答案中会令人困惑。
    • @Curious 看看 Nicol Bolas 的这个答案。它为我澄清了很多话题stackoverflow.com/a/9552880/4884487
    • 这是我在阅读有关 xvalues、prvalues 和 glvalues 时首先阅读的内容之一 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-02
    • 2011-06-07
    • 2012-06-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多