使用const-cast通过非const引用临时扩展生命周期

时间:2019-12-04 12:18:06

标签: c++ language-lawyer object-lifetime const-cast

这是最近出现的事情,我认为它显然不应该起作用:

#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的最终输出是相同的:

  

我正在使用非const引用临时文件! 5正在删除!

通过某种机制扩展了所得shared_ptr的寿命,以匹配std::shared_ptr<int>& ptr的寿命。

发生了什么事

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

此外,Microsoft拥有一个扩展,该扩展允许非const引用延长绑定临时临时文件的寿命,但是即使禁用了该扩展,该行为似乎仍然存在,此外,它还在Clang和GCC中出现。

根据this answer,我认为该临时对象是隐式创建为const的,因此尝试修改ptr所引用的对象可能是未定义的行为,但是我不确定知识告诉我任何有关延长寿命的原因。我的理解是,修改是UB的const的行为,而不仅仅是对它的非const引用。

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

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

  2. static_cast<const Type&>(...)将该prvalue实例化为const xvalue,其生存期等于内部表达式。然后,我们创建对该const xvalue的const lvalue引用。 xvalue的生存期得以延长,以匹配const lvalue引用的生存期。

  3. const_cast<Type&>(...)产生一个左值引用,然后将其分配给ptr const左值引用随即失效,并带有物化的左值。

  4. 我尝试阅读悬而未决的参考书ptr,并且发生了不好的事情。

我的理解有什么问题?为什么不出现斜体字呢?

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

2 个答案:

答案 0 :(得分:2)

任何引用都可以延长对象的寿命。但是,非const引用不能像您的示例那样绑定到临时目录。您引用的Microsoft扩展不是“通过非const引用延长生存期”,而是“让非const引用绑定到临时对象”。他们具有该扩展功能,以便与他们自己以前的损坏的编译器版本向后兼容。

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

更多信息:Do *non*-const references prolong the lives of temporaries?

答案 1 :(得分:0)

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

绑定到普通的非常量引用并进行修改是完成同一件事的另一种方式。它需要强制转换,但是与上面的非常相似。