通过使用 const-cast 的非常量引用来延长临时的寿命

Lifetime extension of temporary by non-const reference using const-cast

本文关键字:常量 const-cast 非常 引用      更新时间:2023-10-16

这是最近出现的东西,我觉得它不应该像它显然那样工作:

#include <iostream>
#include <memory>
int main()
{
std::shared_ptr<int>& ptr = const_cast<std::shared_ptr<int>&>(
static_cast<const std::shared_ptr<int>&>(
std::shared_ptr<int>(
new int(5), [](int* p) {std::cout << "Deleting!"; *p = 999;  delete(p); }
)
)
);
std::cout << "I'm using a non-const ref to a temp! " << *ptr << " ";
}

此处不需要使用shared_ptr,但自定义删除器可以轻松演示结果对象的生存期。Visual Studio,Clang和GCC的结果输出是相同的:

我正在使用一个非常量参考来临时! 5 删除!

这意味着通过某种机制,生成的shared_ptr的寿命已延长以匹配std::shared_ptr<int>& ptr的寿命。

发生了什么事情?

现在,我知道临时的生存期将延长到常量引用情况下的引用的生存期。但是唯一的命名对象是一个非常量引用,我希望所有其他中间表示的生存期仅等于初始化表达式。

此外,Microsoft有一个扩展,它允许非常量引用延长绑定临时的生存期,但即使禁用该扩展,这种行为似乎也存在,此外,也出现在 Clang 和 GCC 中。

根据这个答案,我相信临时是隐式创建的const,所以试图修改ptr引用的对象可能是未定义的行为,但我不确定知识告诉我为什么寿命被延长。我的理解是,这是修改UB 的常量的行为,而不是简单地对它进行非常量引用。

我对应该发生的事情的理解如下:

  1. Type()创建一个没有 cv 规范的 prvalue。

  2. static_cast<const Type&>(...)将该 prvalue 具体化为具有等于内部表达式的 const x值。然后,我们创建一个对该常量 xvalue 的常量左值引用。x值的生存期延长以匹配常量左值引用的生存期。

  3. const_cast<Type&>(...)生成一个左值引用,然后将其分配给ptr然后,常量左值引用过期,并随之而来具体化的 xvalue。

  4. 我试着阅读悬而未决的参考ptr,坏事发生了。

我的理解有什么问题?为什么斜体中的位不发生?

作为一个额外的奖励问题,我是否正确地认为底层对象是常量,并且任何通过此路径修改它的尝试都会导致未定义的行为?

任何引用都可以延长对象的生存期。 但是,非常量引用不能绑定到临时引用,如示例中所示。 您引用的Microsoft扩展不是"通过非常量引用延长生存期",而是"让非常量引用绑定到临时引用"。 他们具有该扩展,以便与自己以前损坏的编译器版本向后兼容。

通过强制转换,您强制将非常量引用绑定到临时引用,这似乎不是无效的,只是不寻常,因为它不能直接完成。 完成该绑定后,非常量引用的生存期延长与常量引用的生存期延长相同。

更多信息:*非*常量引用会延长临时人员的寿命吗?

链接的文章显然是错误的。临时对象(不一定(是常量对象。它不能绑定到非常量引用这一事实并不重要。它可以绑定到右值引用,而无需强制转换,并通过此类引用进行修改。这样做没有UB。也可以临时调用非常量成员函数。移动语义的整个概念就是基于这一事实。

绑定到正常的非常量引用并通过它进行修改是伴随同一事物的不同方法。它需要演员表,但除此之外,它与上述非常相似。