当前位置: 首页 > 知识库问答 >
问题:

为什么const引用会延长右值的生存期?

颛孙霖
2023-03-14

为什么C委员会决定常量引用应该延长临时人员的寿命?

这个事实已经在网上被广泛讨论过了,包括在stackoverflow上。解释这种情况的权威资源可能是这个GoTW:

GotW#88:“最重要常量”的候选人

这种语言特征的基本原理是什么?知道了吗?

(另一种选择是,临时人员的寿命不会因任何引用而延长。)

我个人最喜欢的理论是,这种行为允许对象隐藏实现细节。有了这个规则,成员函数可以在返回一个值或一个对内部已经存在的值的常量引用之间切换,而不需要对客户端代码进行任何更改。例如,矩阵类可以返回行向量和列向量。为了最小化副本,根据实现(行主和列主),可以将其中一个作为引用返回。不能通过引用返回的任何一个都必须通过复制并返回该值来返回(如果返回的向量是连续的)。库编写者可能希望在将来有余地更改实现(行主调与列主调),并防止客户端编写强烈依赖于实现是行主调还是列主调的代码。通过要求客户端接受返回值作为const ref,矩阵类可以返回const ref或值,而不需要对客户端代码进行任何更改。无论如何,如果最初的原理是已知的,我想知道它。

共有2个答案

左康安
2023-03-14

您不是在质疑为什么const引用被允许绑定到临时对象,而仅仅是为什么它们延长了这些临时对象的生命周期

考虑这个代码:

struct A
{
    void foo() const;
};

A bar();

const A& a = bar();

a.foo();               // (1)

如果bar()返回的临时对象的生存期没有延长,那么a的任何用法(如第(1)行所示)都将导致未定义的行为。这将使绑定到临时对象的非参数const引用完全无效。

编辑(回应OP的评论):

因此,真正的问题应该是为什么允许常量引用变量(不是函数参数)绑定到临时变量。我不知道它最初的理由(理查德·霍奇斯的答案可能是唯一正确的答案),但它为我们提供了一个有用的特性。考虑以下例子:

struct B
{
    virtual void foo() const;
};

B bar();

const B& b = bar();

b.foo();               // (1)

这个例子与前一个例子的唯一区别是B::foo()是虚拟的。现在,如果我们决定引入一个新类D作为B的子类,并将bar()的返回类型从B更改为D

struct B
{
    virtual void foo() const;
};

struct D : B
{
    virtual void foo() const;
};

//B bar();
D bar();

const B& b = bar();

b.foo(); // This will call D::foo()

// In the end the temporary bound by b will be correctly destroyed
// using the destructor of D.

因此,将常量引用绑定到临时对象简化了对由值返回的对象的动态多态性的利用。

施彦
2023-03-14

它是1993年提出的。它的目的是消除在与引用绑定时对临时表的不一致处理。

那时,还没有RVO这样的东西,所以简单地禁止将临时绑定到引用会影响性能。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1993/N0345.pdf

 类似资料:
  • 我遇到了一些代码,在一个以lambda传递的函数参数中使用const右值引用。令人困惑的部分是,它随后对传递的参数有一个调用。类似于这样: 传入常量值引用,然后对其调用std::move的目的是什么?因此,我尝试了一个更简单的代码片段,以了解在这种情况下会发生什么 产生了以下输出: 在godbolt(此处)检查组件,确认发生了什么。

  • 我有以下代码 我已经阅读了(MSDN) 关键字<code>constexpr</code>在C 11中引入,并在C 14中进行了改进。它表示常量表达式。与<code>const</code>一样,它可以应用于变量,因此如果任何代码试图修改值,就会引发编译器错误。 阅读后,我认为可以使用代替,但是对于上面的代码,我收到一个编译器错误,指出 当 被 替换时,它可以正常工作。我不理解这种行为;有人能说出

  • 左值(赋值操作符“=”的左侧,通常是一个变量)与右值(赋值操作符“=”的右侧,通常是一个常数、表达式、函数调用)之间的差别可以追溯到 Christopher Strachey (C++的祖先语言CPL之父,指称语义学之父)时代。在C++中,左值可被绑定到非const引用,左值或者右值则可被绑定到const引用。但是却没有什么可以绑定到非const的右值(译注:即右值无法被非const的引用绑定),

  • 我读了Patterson的以下声明 随着处理器进入更长的流水线并在每个时钟周期发出多条指令,分支延迟变得更长,单个延迟槽是不够的。 我可以理解为什么“每个时钟周期发出多条指令”会导致单个延迟槽不足,但我不知道为什么“更长的管道”会导致它。 另外,我不明白为什么更长的管道会导致分支延迟变得更长。即使管道更长(完成一条指令的步骤),也不能保证周期会增加,那么为什么分支延迟会增加呢?

  • 问题内容: 我很难处理Java垃圾回收问题并解释日志。 我的应用程序要求GC的时间不要超过2秒,理想情况下是少于100ms。 根据先前的一些建议,我正在尝试以下命令行选项: 该应用程序具有大量长期存储的对象,这些对象保存在ConcurrentLinkedHashMap中。我偶尔会出现长时间的停顿,在最坏的情况下可能会长达10秒(这是倒数第二次,如下面的GC日志所示)! 这是我得到的一些输出: 我已