【问题标题】:c++17: A temporary object never destroyedc++17:一个永远不会被销毁的临时对象
【发布时间】:2019-07-27 14:16:36
【问题描述】:
struct Base {
    Base() {
        std::cout << "Inside:  " << __PRETTY_FUNCTION__ << std::endl;
    }
    ~Base() {
        std::cout << "Inside:  " << __PRETTY_FUNCTION__ << std::endl;
    }
};

struct BaseWrapper {
    const Base &b;
};

int main()
{
    {
        auto *w = new BaseWrapper{{}};
        std::cout << "Inside:  " << __PRETTY_FUNCTION__ << std::endl;
        delete w;
    }
    return 0;
}

当我用 C++11 或 C++14 编译时,上面的代码可以正常工作,但是当我用 C++17 编译时,它给了我这样的结果:

Inside:  Base::Base()                                                                                                                                                              
Inside:  int main()

如您所见,从未调用过 Base::~Base()。这对我来说没有多大意义。我已经在 Ubuntu 18.04.1 LTS 上使用 GCC 7.3.0 以及 OnlineGDB 测试了这段代码。它们都给出相同的结果。

我只是想知道这是 C++17 中的一个新特性还是一个错误?

更新: 我很清楚w-&gt;b 是一个悬空引用。实际上,我特意写了这段代码就是为了说明这一点。然后,我在测试时发现了这个问题。

我真正想知道的是这个问题有多严重,以防我不得不坚持使用 GCC 7.3?或者是否有任何其他方法可以重现相同的问题?还是缺陷报告?

【问题讨论】:

标签: c++ gcc language-lawyer c++17 object-lifetime


【解决方案1】:

看起来像一个错误,可能与引用生命周期延长和堆分配的对象有关。

引用成员的聚合初始化存在奇怪的生命周期扩展规则。

您可以升级编译器或简单地添加 BaseWrapper(Base const&amp; bin):b(bin){} ctor 并观察错误消失。

【讨论】:

  • 即使auto *w = new BaseWrapper{Base{}}; 也能解决这个问题。
  • @aep 当然,但这需要自动处理基本包装器的所有问题。我宁愿希望 basewrapper 可以安全使用而不是疯狂。再说一次,悬空引用是疯狂的
【解决方案2】:

显然,创建但未销毁的临时是编译器错误 - 除非标准说明行为未定义。但是,该示例已明确定义。标准[class.temporary]中的相关规则:

当实现引入具有非平凡构造函数([class.default.ctor],[class.copy.ctor])的类的临时对象时,它应确保为临时对象调用构造函数.类似地,析构函数应该被调用一个具有非平凡析构函数的临时函数([class.dtor])。临时对象被销毁作为评估完整表达式([intro.execution])的最后一步,该完整表达式(从词法上)包含它们被创建的点。 ...

在三种情况下,临时对象在与完整表达式结尾不同的点被销毁。 ...

第三个上下文是当一个引用被绑定到一个临时对象时......

此生命周期规则的例外情况是:

  • ...

  • 绑定到 new-initializer ([expr.new]) 中的引用的临时持续存在,直到包含 new-initializer 的完整表达式完成

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-11
    • 2015-01-10
    • 2018-05-06
    • 2019-05-14
    • 1970-01-01
    • 1970-01-01
    • 2019-08-19
    相关资源
    最近更新 更多