c++17: 一个从未被销毁的临时对象
c++17: A temporary object never destroyed
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->b
是一个悬空引用。其实,我特意写了这段代码就是为了表明这一点。然后,我在测试的时候发现了这个问题。
我真正想知道的是这个问题有多严重,以防我必须坚持使用 GCC 7.3?或者是否有任何其他方法可以重现相同的问题?或缺陷报告?
看起来像一个错误,可能与引用生命周期延长和堆分配对象有关。
引用成员的聚合初始化存在奇怪的生命周期延长规则。
您可以升级您的编译器或简单地添加一个 BaseWrapper(Base const& bin):b(bin){}
构造器并观察错误消失。
显然,创建但未销毁的临时文件是一个编译器错误 - 除非标准说明该行为未定义。但是,该示例定义明确。标准中的相关规则 [class.temporary]:
When an implementation introduces a temporary object of a class that has a non-trivial constructor ([class.default.ctor], [class.copy.ctor]), it shall ensure that a constructor is called for the temporary object. Similarly, the destructor shall be called for a temporary with a non-trivial destructor ([class.dtor]). Temporary objects are destroyed as the last step in evaluating the full-expression ([intro.execution]) that (lexically) contains the point where they were created. ...
There are three contexts in which temporaries are destroyed at a different point than the end of the full-expression. ...
The third context is when a reference is bound to a temporary object ...
The exceptions to this lifetime rule are:
...
A temporary bound to a reference in a new-initializer ([expr.new]) persists until the completion of the full-expression containing the new-initializer.
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->b
是一个悬空引用。其实,我特意写了这段代码就是为了表明这一点。然后,我在测试的时候发现了这个问题。
我真正想知道的是这个问题有多严重,以防我必须坚持使用 GCC 7.3?或者是否有任何其他方法可以重现相同的问题?或缺陷报告?
看起来像一个错误,可能与引用生命周期延长和堆分配对象有关。
引用成员的聚合初始化存在奇怪的生命周期延长规则。
您可以升级您的编译器或简单地添加一个 BaseWrapper(Base const& bin):b(bin){}
构造器并观察错误消失。
显然,创建但未销毁的临时文件是一个编译器错误 - 除非标准说明该行为未定义。但是,该示例定义明确。标准中的相关规则 [class.temporary]:
When an implementation introduces a temporary object of a class that has a non-trivial constructor ([class.default.ctor], [class.copy.ctor]), it shall ensure that a constructor is called for the temporary object. Similarly, the destructor shall be called for a temporary with a non-trivial destructor ([class.dtor]). Temporary objects are destroyed as the last step in evaluating the full-expression ([intro.execution]) that (lexically) contains the point where they were created. ...
There are three contexts in which temporaries are destroyed at a different point than the end of the full-expression. ...
The third context is when a reference is bound to a temporary object ...
The exceptions to this lifetime rule are:
...
A temporary bound to a reference in a new-initializer ([expr.new]) persists until the completion of the full-expression containing the new-initializer.