英文:
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 theunique_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 theunique_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
// main.cpp
#include "BB.h"
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 definingBB
.AA
must be complete when definingBB::~BB
.- Similarly, leaving
BB::BB
implicitly defined or= default
ed triggers astatic_assert
bydefault_delete
in g++ and clang++ since it would then need to defineunique_ptr<AA, default_delete<AA>>
s destructor too early:~unique_ptr() { default_delete<AA>{}(the_pointer); } // oups, sizeof(AA) unknown
- Dererring the definitions of
BB
member functions untilAA
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
英文:
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 <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 shoudln't compile but it was discussed below that P0859r0 brought it about
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论