4 ms·
Unfortunately this advice fails for forward references, which is one of the most common reasons for using a pointer (e.g. the pimpl idiom). An example: #in
by ridiculous_fish 11y ago
Unfortunately this advice fails for forward references, which is one of the most common reasons for using a pointer (e.g. the pimpl idiom). An example:
#include <memory>
struct Gadget;
struct BlackBox {
std::unique_ptr<Gadget> contents;
~BlackBox();
};
void func() {
BlackBox b;
}
This will fail to compile (for reasons I don't understand - can anyone explain?) But replace the unique_ptr with a raw pointer and it will be fine.
- nice_byte 11y agoI think it should work if you define the ctor and dtor for BlackBox in a separate .cpp file where you include gadget.h. The reason it fails to compile is probably that the implicitly generate constructor of BlackBox (which is inline, I guess) may need to call the dtor of unique_ptr in case something in the constructor throws an exception.
- ridiculous_fish 11y agoYou're right, that works! Thanks, I would not have arrived at that on my own.
- wuch 11y agoThis is required by the standard. unique_ptr<Gadget> in your example uses default_delete<Gadget> deleter, which in turn requires that type Gadget is complete at the point where this deleter is used, otherwise program is ill-formed. Intention is to avoid undefined behaviour in situation where you delete an object with incomplete class type at the point of deletion, that has a non-trivial destructor or a deallocation function. Edit: An deleter is used because of default generated copy constructor.
- ridiculous_fish 11y agoThat doesn't sound right. unique_ptr does not have a copy constructor so there should not be an implicit copy ctor (am I right?) Exception handling in the default ctor I think is right.
- cmma 11y agoThe reason why that code fails is explained by gcc's unique_ptr implementation: /// Primary template of default_delete, used by unique_ptr template<typename _Tp> struct default_delete { ... void operator()(_Tp* __ptr) const { static_assert(!is_void<_Tp>::value, "can't delete pointer to incomplete type"); static_assert(sizeof(_Tp)>0, "can't delete pointer to incomplete type"); delete __ptr; } }; Basically, invoking "operator delete" on a pointer to an incomplete type (aka opaque pointer) is usually undefined. Calling "operator new" on an incomplete type is not possible (because its size is 0). Since unique_ptr deals with allocation/deallocation, and doesn't just copy pointer values around, it's not fully comparable to a plain pointer. nice_byte proposed a good solution: if you can, abstract the new/delete logic to another file, in which the complete type of "Gadget" is known.
- ridiculous_fish 11y agoThat's true, but it's not obvious why anything in that code calls new or delete, since the destructor is defined elsewhere. So why does the constructor invoke operator delete? The missing puzzle piece is apparently exception handling: if the implicit constructor throws (which of course it cannot) it must delete this object (which of course will be null). Sadly this silly requirement persists even when exception handling is disabled at the compiler level.