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

AnA*_*ons 2 c++ llvm clang c++23

归结为:

#include <memory>
class dummy;

std::unique_ptr<dummy> test;

class dummy {

};
Run Code Online (Sandbox Code Playgroud)

当使用 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.
Run Code Online (Sandbox Code Playgroud)

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

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

#include <memory>
#include <llvm/ADT/APFloat.h>
Run Code Online (Sandbox Code Playgroud)

将 llvm-14 和 libstdc++-12-dev 与 Ubuntu 22.04.1 LTS 中的 clang++-14 一起使用

编译:

clang++ -std=c++2b bugllvm.cpp
Run Code Online (Sandbox Code Playgroud)

使用-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;
Run Code Online (Sandbox Code Playgroud)

APFloat此时已声明但尚未定义:

class APFloat;
Run Code Online (Sandbox Code Playgroud)

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

也许是别的东西。

use*_*522 6

问题不在于std::unique_ptr作为会员使用。实际问题在operator=第 625 行的实现中的类定义中:

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

DoubleAPFloat析构函数调用导致定义析构函数。std::unique_ptr<APFloat[]>这需要实例化 的析构函数。该析构函数的实例化需要 的实例化,而std::default_delete<APFloat[]>s的operator()实例化又要求APFloat完整。

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

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

<llvm/ADT/APFloat.h>所以这是一个编译器不必诊断的错误。


这同样适用于您减少的测试用例。的定义test需要定义析构函数,因为需要析构该变量。同样,隐式实例化可以紧接在变量定义之后或翻译单元的末尾放置。如果没有最后的定义dummy,两个实例化点都是错误的,因此现在需要编译器对其进行诊断。