Ben*_*Ben 1 c++ language-lawyer
我对调用约定的理解是,函数在调用者要求的地方(或者在传统的地方?)构造它们的结果。考虑到这一点,这让我感到惊讶:
#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}; }
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/rqb1fKq3x
虽然可以constructAt编译,但很高兴放置new一个 null unique_ptr<X>,foo()但无法编译,因为编译器想要实例化unique_ptr<X>::~unique_ptr()。我理解为什么它不能实例化该析构函数(因为就语言而言,它需要遵循nullptrd'tor 的非分支,然后删除内存 [/sf/ask/1996536531/ /为什么唯一的ptrtunique-ptr需要t的定义])。基本上没有完整的X, 的unique_ptr析构函数就会被 SFINAE 掉(对吗?)。但是为什么返回值的函数必须知道如何破坏该值呢?难道调用者不是必须破坏它的人吗?
显然,我的职责constructAt和foo职能在道德上并不等同。这种语言是迂腐的,还是有一些代码路径(例外?)必须foo()破坏该值?
在您的具体情况下,无法调用析构函数。然而,该标准以更一般的术语指定了可能调用析构函数的情况。如果可能调用析构函数,则它需要一个定义(即使没有可以调用它的路径),因此这将导致隐式实例化,在您的情况下会失败,因为std::unique_ptr<X>析构函数的实例化需要X完成。
特别是,析构函数可能会为语句中的每个结果对象调用return。
我认为这种选择的原因在CWG Issue 2176中有描述:一般来说,函数中可能存在局部变量和临时变量,这些变量和临时变量在return构造语句的结果对象后会被销毁。但是,如果这些对象之一的销毁引发异常,那么已经构造的结果对象也应该被销毁。这需要定义析构函数。
然后, CWG 问题 2426使得析构函数可能被调用,即使由于上述推理而没有实际调用,与实现一致。我认为做出这个选择只是因为它不需要编译器方面做出任何额外的决策并且已经实现了。