为什么 = 默认成员初始值设定项请求 unique_ptr 析构函数的实例化,而 {} 则不然?

发布于 2025-01-13 00:45:18 字数 2157 浏览 0 评论 0原文

这是这个问题的后续内容: Does PIMPL idiom 实际上可以使用 std::unique_ptr?

完整示例使用多个文件,所以为了这个问题我在这里减少一下。完整的工作示例在这里: https://wandbox.org/permlink/AepAJYkbRU4buDoJ 和完整的非-这里的工作示例: https://wandbox.org/permlink/0kP23UYJbSaUvJgS

较短的示例是:

#include <memory>

struct A;

struct B {
    ~B();
    std::unique_ptr<A> p =  nullptr;
};

产生错误

In file included from <source>:1:
In file included from /opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/memory:76:
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/unique_ptr.h:83:16: error: invalid application of 'sizeof' to an incomplete type 'A'
        static_assert(sizeof(_Tp)>0,
                      ^~~~~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/unique_ptr.h:361:4: note: in instantiation of member function 'std::default_delete<A>::operator()' requested here
          get_deleter()(std::move(__ptr));
          ^
<source>:7:29: note: in instantiation of member function 'std::unique_ptr<A>::~unique_ptr' requested here
    std::unique_ptr<A> p =  nullptr;
                            ^
<source>:3:8: note: forward declaration of 'A'
struct A;
       ^
1 error generated.

使用 {} 编译时:

#include <memory>

struct A;

struct B {
    ~B();
    std::unique_ptr<A> p{nullptr};
};

没有错误:https://godbolt.org/z/snsfsjdqE

什么有什么区别吗?为什么 std::unique_ptr p = nullptr; 需要实例化 unique_ptr 析构函数,但是 std::unique_ptr; p{nullptr}; 不是吗?

PS:我使用 clang 来获得更清晰的错误消息。与 gcc 的结果相同。到目前为止我尝试过的所有版本。

This is a follow up of this question: Does PIMPL idiom actually work using std::unique_ptr?

The full example uses multiple files, so for the sake of this question I will reduce it here. The full working example is here: https://wandbox.org/permlink/AepAJYkbRU4buDoJ and the full non-working example here: https://wandbox.org/permlink/0kP23UYJbSaUvJgS.

The shorter example is:

#include <memory>

struct A;

struct B {
    ~B();
    std::unique_ptr<A> p =  nullptr;
};

Which produces the error:

In file included from <source>:1:
In file included from /opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/memory:76:
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/unique_ptr.h:83:16: error: invalid application of 'sizeof' to an incomplete type 'A'
        static_assert(sizeof(_Tp)>0,
                      ^~~~~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/unique_ptr.h:361:4: note: in instantiation of member function 'std::default_delete<A>::operator()' requested here
          get_deleter()(std::move(__ptr));
          ^
<source>:7:29: note: in instantiation of member function 'std::unique_ptr<A>::~unique_ptr' requested here
    std::unique_ptr<A> p =  nullptr;
                            ^
<source>:3:8: note: forward declaration of 'A'
struct A;
       ^
1 error generated.

While using {} compiles:

#include <memory>

struct A;

struct B {
    ~B();
    std::unique_ptr<A> p{nullptr};
};

No errors: https://godbolt.org/z/snsfsjdqE

What is the difference? Why does std::unique_ptr<A> p = nullptr; require to instantiate the unique_ptr destructor, but std::unique_ptr<A> p{nullptr}; does not?

PS: I used clang for a clearer error message. Same results with gcc. All versions I tried so far.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

空心↖ 2025-01-20 00:45:18

这似乎是一个与 std::unique_ptr 无关的语言问题。
我想说的是 GCC 和 Clang 中的一个错误。

这是没有 std::unique_ptr 的。

这个编译:

template<typename T>
struct uptr {
    uptr(nullptr_t) {}
    ~uptr() {
        delete (new T);
    }
};

class A
{
public:
  A();
  ~A();
private:
  class B;
  uptr<B> m_b {nullptr}; // the dtor of uptr is not instantiated yet
};

但是this 无法编译:

template<typename T>
struct uptr {
    uptr(nullptr_t) {}
    ~uptr() {
        delete (new T);
    }
};

class A
{
public:
  A();
  ~A();
private:
  class B;
  uptr<B> m_b = nullptr; // the dtor of uptr tries to be instantiated here
};

我的直觉是实例化在这两种情况下,uptr 的析构函数都应延迟到 A 的析构函数的实例化。所以这两种情况都应该通过编译。这就是为什么我认为这是编译器中的一个错误,在第二种情况下,在 A 的析构函数出现之前且 B 仍然不完整时,试图过早实例化析构函数。

有趣的是,使用大括号大多数编译器(GCC、Clang、MSVC)确实会延迟析构函数的实例化。我的支票中只有 ICC 拒绝了该代码 - 我想说,这是错误的。但是,如果使用赋值符号构建大多数编译器(GCC、Clang、ICC)过早实例化析构函数,只有我的检查中的 MSVC 仍然延迟它 - 我相信它应该如此。如果我们使用 同时使用大括号和赋值符号,Clang 将加入 MSVC,从而延迟析构函数实例化。 GCC 和 ICC 仍会尝试实例化它。

另请参阅相关SO post 解释了为什么我认为 GCC 和 Clang 在这里是错误的。

It seems to be a language issue unrelated to std::unique_ptr.
I would say a bug in both GCC and Clang.

Here it is without std::unique_ptr.

This one compiles:

template<typename T>
struct uptr {
    uptr(nullptr_t) {}
    ~uptr() {
        delete (new T);
    }
};

class A
{
public:
  A();
  ~A();
private:
  class B;
  uptr<B> m_b {nullptr}; // the dtor of uptr is not instantiated yet
};

But this doesn't compile:

template<typename T>
struct uptr {
    uptr(nullptr_t) {}
    ~uptr() {
        delete (new T);
    }
};

class A
{
public:
  A();
  ~A();
private:
  class B;
  uptr<B> m_b = nullptr; // the dtor of uptr tries to be instantiated here
};

My intuition is that the instantiation of uptr's destructor shall be delayed to the instantiation of A's destructor, in both cases. So both cases shall pass compilation. This is why I believe it is a bug in the compiler, trying to instantiate the destructor too early, before A's destructor appears and when B is still incomplete, in the second case.

It is interesting to note that with the curly brackets most compilers (GCC, Clang, MSVC) indeed delay the instantiation of the destructor. Only ICC in my check rejects the code - wrongly, I would say. However, if constructing with assignment sign most compilers (GCC, Clang, ICC) instantiate the destructor too early, only MSVC in my check still delays it - as it should, I believe. In case we use both the curly brackets and assignment sign, Clang would join MSVC, delaying the destructor instantiation. GCC and ICC would still try to instantiate it.

See also a related SO post that explains why I believe GCC and Clang are wrong here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文