std::unique_ptr<incomplete_type> without custom deleter

huangapple go评论76阅读模式
英文:

std::unique_ptr<incomplete_type> without custom deleter

问题

以下是翻译好的部分:

"The following https://godbolt.org/z/5qd8WbYz9 doesn't work and after reading the duplicate I thought I would have to define an custom deleter

However, I managed to get it working https://godbolt.org/z/dzYvsdKP1 by

  • adding a default constructor implemented in BB.cpp (to avoid implicit in-class default constructor)
  • removing the {nullptr} in-class initialization of the unique_ptr

Can someone explain why the {nullptr} make's it break?

It's important to note that std::default_deleter<AA> can be instantiated even when AA is incomplete.

//BB.h
#include <memory>
struct AA;
struct BB
{
    
    // B(); <- needed
	~BB();
	std::unique_ptr<AA> u{ nullptr }; // need to remove {nullptr} initializer
};
// BB.cpp
#include "BB.h"
struct AA {};
BB::~BB() = default;
// BB::BB() = default; <- needed
英文:

The following https://godbolt.org/z/5qd8WbYz9 doesn't work and after reading the duplicate I thought I would have to define an custom deleter

However, I managed to get it working https://godbolt.org/z/dzYvsdKP1 by

  • adding a default constructor implemented in BB.cpp (to avoid implicit in-class default constructor)
  • removing the {nullptr} in-class initialization of the unique_ptr

Can someone explain why the {nullptr} make's it break?

It's important to note that std::default_deleter&lt;AA&gt; can be instantiated even when AA is incomplete.

//BB.h
#include &lt;memory&gt;
struct AA;
struct BB
{
    
    // B(); &lt;- needed
	~BB();
	std::unique_ptr&lt;AA&gt; u{ nullptr }; // need to remove {nullptr} initializer
};
// BB.cpp
#include &quot;BB.h&quot;
struct AA {};
BB::~BB() = default;
// BB::BB() = default; &lt;- needed
// main.cpp
#include &quot;BB.h&quot;
int main()
{
    BB bb{};
}

答案1

得分: 3

AA 在定义 BB::~BB 时不能是不完整类型,因为在调用 std::default_delete::operator()AA 必须是完整类型(这将在 BB::~BB 中发生)。否则,程序将是不合法的。

cppreference:
> 这个要求的原因是,在 C++ 中对不完整类型调用 delete 如果完整类类型具有非平凡析构函数或解分配函数,则是未定义行为,因为编译器无法知道是否存在这样的函数并且必须调用它们。

这种情况与使用 pimpl 惯用法实现某些东西时的情况相同。

  • 在定义 BB 时,AA 可以是不完整的。
  • 在定义 BB::~BB 时,AA 必须是完整的。
  • 同样,将 BB::BB 的定义推迟到 AA 定义时是惯用的解决方案。
英文:

AA can not be an incomplete type at the time BB::~BB is defined since AA must be a complete type at the time std::default_delete::operator() is called (which will happen in BB::~BB. Otherwise, the program is ill-formed.

cppreference:
> The reason for this requirement is that calling delete on an incomplete type is undefined behavior in C++ if the complete class type has a nontrivial destructor or a deallocation function, as the compiler has no way of knowing whether such functions exist and must be invoked.

The situation is the same as when you are implementing something using the pimpl idiom.

  • AA can be incomplete when defining BB.
  • AA must be complete when defining BB::~BB.
  • Similarly, leaving BB::BB implicitly defined or = defaulted triggers a static_assert by default_delete in g++ and clang++ since it would then need to define unique_ptr&lt;AA, default_delete&lt;AA&gt;&gt;s destructor too early:
    ~unique_ptr() { default_delete&lt;AA&gt;{}(the_pointer); } // oups, sizeof(AA) unknown
    
  • Dererring the definitions of BB member functions until AA is defined is the idiomatic solution.

答案2

得分: -1

So pimpl with assignment initialization doesn't compile for any vendors anymore (although it used to on clang and msvc). But with brace initialization it's only broken on msvc (not conformant I guess)

https://godbolt.org/z/KchervEY3

#include <memory>
struct incomplete_type;
struct A
{
    A();
    ~A();
    std::unique_ptr<incomplete_type> u1; // always fine
    std::unique_ptr<incomplete_type> u2{ nullptr }; // error in msvc only
    //std::unique_ptr<incomplete_type> u3 = nullptr; // always error
};
int main()
{
    A bb{};
}

I'm still not sure exactly why assignment initialization shouldn't compile but it was discussed below that P0859r0 brought it about

https://developercommunity.visualstudio.com/t/vs2022-173-Preview-std::unique_ptr-need/10107855#T-N10306156

英文:

So pimpl with assignment initialization doesn't compile for any venders anymore (although it used to on clang and msvc). But with brace initialization it's only broken on msvc (not conformant I guess)

https://godbolt.org/z/KchervEY3

#include &lt;memory&gt;
struct incomplete_type;
struct A
{
    A();
    ~A();
    std::unique_ptr&lt;incomplete_type&gt; u1; // always fine
    std::unique_ptr&lt;incomplete_type&gt; u2{ nullptr }; // error in msvc only
    //std::unique_ptr&lt;incomplete_type&gt; u3 = nullptr; // always error
};
int main()
{
    A bb{};
}

I'm still not sure exactly why assignment initialization shoudln't compile but it was discussed below that P0859r0 brought it about

https://developercommunity.visualstudio.com/t/vs2022-173-Preview-std::unique_ptr-need/10107855#T-N10306156

huangapple
  • 本文由 发表于 2023年7月3日 17:19:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76603419.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定