我对调用约定的理解是,函数在调用者要求它们的结果(或者在传统的地方?)构造它们的结果。考虑到这一点,这让我感到惊讶:
#include <memory>
struct X; // Incomplete type.
// Placement-new a null unique_ptr in-place:
void constructAt(std::unique_ptr<X>* ptr) { new (&ptr) std::unique_ptr<X>{nullptr}; }
// Return a null unique_ptr:
std::unique_ptr<X> foo() { return std::unique_ptr<X>{nullptr}; }https://godbolt.org/z/rqb1fKq3x,而constructAt编译,愉快地放置-new的空unique_ptr<X>,foo()不编译,因为编译器想要实例化unique_ptr<X>::~unique_ptr()。我理解它为什么不能实例化这个析构函数(因为就语言而言,它需要跟随d‘’tor的非nullptr分支,然后删除内存nullptr)。基本上,如果没有一个完整的X,unique_ptr的析构函数就会消失(对吧?)。但是为什么返回值的函数必须知道如何销毁该值呢?调用者不是必须销毁它的函数吗?
显然,我的constructAt和foo函数在道义上并不等同。这种语言是迂腐的,还是有一些代码路径(例外?)foo()必须在哪里销毁这个值?
发布于 2022-04-07 18:36:56
在特定情况下,不可能调用析构函数。但是,该标准指定了可能以更一般的方式调用析构函数的情况。如果一个析构函数可能被调用,它需要一个定义(即使没有可以调用它的路径),因此这将导致隐式实例化,这在您的情况下是失败的,因为std::unique_ptr<X>析构函数的实例化要求X是完整的。
特别是,对于return语句中的每个结果对象,都可能调用析构函数。
我认为CWG第2176期描述了这种选择的原因:通常,在构造return语句的结果对象之后,函数中可能有局部变量和临时变量被破坏。但是,如果销毁其中一个对象会抛出异常,那么已经构造的结果对象也应该被销毁。这需要定义析构函数。
然后,CWG第2426期使析构函数可能被调用,即使由于上述推理没有实际调用,这与实现是一致的。我认为做出这一选择只是因为它不需要编译器做出任何额外的决策,而且已经实现了。
https://stackoverflow.com/questions/71787027
复制相似问题