类型不完整的唯一指针 - c++2b?



它归结为:

#include <memory>
class dummy;
std::unique_ptr<dummy> test;
class dummy {
};

当使用 clang++-14 和 c++2b 编译时:

它简要地给出了:

clang++ ../bugl.cpp -I /usr/lib/llvm-14/include  -std=c++2b
In file included from ../bugl.cpp:1:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/memory:76:
/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/unique_ptr.h:93:16: error: invalid application of 'sizeof' to an incomplete type 'dummy'
static_assert(sizeof(_Tp)>0,
^~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/unique_ptr.h:396:4: note: in instantiation of member function 'std::default_delete<dummy>::operator()' requested here
get_deleter()(std::move(__ptr));
^
../bugl.cpp:4:24: note: in instantiation of member function 'std::unique_ptr<dummy>::~unique_ptr' requested here
std::unique_ptr<dummy> test;
^
../bugl.cpp:2:7: note: forward declaration of 'dummy'
class dummy;
^
1 error generated.

但是一旦我们删除了dummy的定义,它就会变得有趣——那么它在任何地方都同样失败。

最初我从 llvm-14 标头中找到它:

#include <memory>
#include <llvm/ADT/APFloat.h>

使用 llvm-14 和 libstdc++-12-dev 以及来自 Ubuntu 22.04.1 LTS 的 clang++-14

编译方式:

clang++ -std=c++2b bugllvm.cpp

使用-std=c++20它可以很好地编译(以及使用 g++ 的 20 和 2b)。

基本上违规的行是这样的:

class DoubleAPFloat final : public APFloatBase {
// Note: this must be the first data member.
const fltSemantics *Semantics;
std::unique_ptr<APFloat[]> Floats;

如果此时声明了APFloat但未定义:

class APFloat;

我的问题是错误在哪里 - 在编译器、标准库、标准本身中?

也许是别的什么。

问题在于没有将std::unique_ptr用作这样的成员。实际问题在第 625 行的operator=实现中的类定义中进一步出现:

DoubleAPFloat &operator=(DoubleAPFloat &&RHS) {
if (this != &RHS) {
this->~DoubleAPFloat();
new (this) DoubleAPFloat(std::move(RHS));
}
return *this;
}

析构函数调用会导致定义DoubleAPFloat的析构函数。这需要实例化std::unique_ptr<APFloat[]>的析构函数。该析构函数的实例化需要实例化std::default_delete<APFloat[]>operator()而这反过来又需要APFloat才能完成。

函数模板专用化的实例化有两个实例化点。一个紧跟在需要它的命名空间范围声明之后(或在需要它的模板实例化点),第二个在翻译单元的末尾。如果选择其中一个而不是另一个会给程序带来不同的含义,那么程序格式不正确,不需要诊断

APFloat稍后在头文件中定义,因此这适用于此处。std::default_delete<APFloat[]>::operator()的实例化在DoubleAPFloat的定义之后立即是格式错误的(其中std::unique_ptr析构函数的实例化有一个实例化点),但它在翻译单元的末尾会形成良好。

因此,这是编译器不必诊断的<llvm/ADT/APFloat.h>中的错误。


这同样适用于简化的测试用例。test的定义需要定义析构函数,因为析构函数是销毁变量所必需的。同样,隐式实例化可以紧跟在变量定义之后或翻译单元的末尾。如果最后没有dummy的定义,两个实例化点的格式都不正确,因此现在需要编译器对其进行诊断。

最新更新