Std::Unique_Ptr With an Incomplete Type Won't Compile

std::unique_ptr with an incomplete type won't compile

Here are some examples of std::unique_ptr with incomplete types. The problem lies in destruction.

If you use pimpl with unique_ptr, you need to declare a destructor:

class foo
{
class impl;
std::unique_ptr<impl> impl_;

public:
foo(); // You may need a def. constructor to be defined elsewhere

~foo(); // Implement (with {}, or with = default;) where impl is complete
};

because otherwise the compiler generates a default one, and it needs a complete declaration of foo::impl for this.

If you have template constructors, then you're screwed, even if you don't construct the impl_ member:

template <typename T>
foo::foo(T bar)
{
// Here the compiler needs to know how to
// destroy impl_ in case an exception is
// thrown !
}

At namespace scope, using unique_ptr will not work either:

class impl;
std::unique_ptr<impl> impl_;

since the compiler must know here how to destroy this static duration object. A workaround is:

class impl;
struct ptr_impl : std::unique_ptr<impl>
{
~ptr_impl(); // Implement (empty body) elsewhere
} impl_;

unique_ptr with forward declared incomplete type won't compile

As mentioned by CuriouslyRecurringThoughts and Jarod42, the issue was due to the assignment of nullptr to m_ageDetectImplPtr

The following code works

    class AgeDetectImpl;
class AgeDetect {
public:
AgeDetect(std::string token);
~AgeDetect();

std::string getAge(std::string imagepath);
std::string getAge(uint8_t* buffer, size_t rows, size_t cols);
std::string getAge(const cv::Mat& image);

private:
std::unique_ptr<AgeDetectImpl> m_ageDetectImplPtr;
};

std::unique_ptr with an incomplete type won't compile

Here are some examples of std::unique_ptr with incomplete types. The problem lies in destruction.

If you use pimpl with unique_ptr, you need to declare a destructor:

class foo
{
class impl;
std::unique_ptr<impl> impl_;

public:
foo(); // You may need a def. constructor to be defined elsewhere

~foo(); // Implement (with {}, or with = default;) where impl is complete
};

because otherwise the compiler generates a default one, and it needs a complete declaration of foo::impl for this.

If you have template constructors, then you're screwed, even if you don't construct the impl_ member:

template <typename T>
foo::foo(T bar)
{
// Here the compiler needs to know how to
// destroy impl_ in case an exception is
// thrown !
}

At namespace scope, using unique_ptr will not work either:

class impl;
std::unique_ptr<impl> impl_;

since the compiler must know here how to destroy this static duration object. A workaround is:

class impl;
struct ptr_impl : std::unique_ptr<impl>
{
~ptr_impl(); // Implement (empty body) elsewhere
} impl_;

C++ Pimpl Idiom Incomplete Type using std::unique_ptr

You can't use defaulted constructors and assignment operators (such as SomeInt( SomeInt&& other ) = default;) declared in header file with Pimpl classes, because the default implementations are inline, and at the point of declaration SomeInt's declaration SomeInt::impl is incomplete, so unique_ptr complains. You have to declare and define out of line (that is, in implementation file) all special member functions yourself.

That is, change SomeInt and SomeComposite declarations as follows:

// SomeInt.h
SomeInt( SomeInt&& other ); // move
SomeInt& operator=( SomeInt&& other ); // move assign

// SomeInt.cpp
// after definition of SomeInt::impl
SomeInt::SomeInt( SomeInt&& other ) = default;
SomeInt& operator=( SomeInt&& other ) = default;

Another option is to create your own Pimpl pointer, as suggested in this answer.

Compile error with gcc when in-class initializing unique_ptr of incomplete type to nullptr

The program, and the default member initialiser in particular, is well-formed. If a compiler refuses to compile, then it is a bug in the compiler as far as I can tell.

I can reproduce the problem with GCC 9.1 but not 9.2 nor trunk, so it appears to have been fixed. With older versions, you may need to give up using the default member initialiser as a workaround.

In-class initialization of std::unique_ptr to an incomplete type

Is it supposed to work as it is in the example?

Yes, the example should work. Defining B::~B after A is complete is sufficient (and necessary).

Some standard rules:

[unique.ptr.general]

... The template parameter T of unique_­ptr may be an incomplete type.

[unique.ptr.dltr.dflt]

The template parameter T of default_­delete may be an incomplete type.


As an aside, minor suggestion:

std::unique_ptr<A>
B::make_a() {
return std::make_unique<A>();
}

std::unique_ptrT incomplete type error

The requirement of complete type at the template instantiation point is due to std::default_delete, so probably you could work-around it by providing a custom deleter.

struct node;         // node_delete need to know 'node' is a type.
struct node_deleter
{ // std::unique_ptr needs 'node_deleter' to be complete.
void operator()(node* ptr); // forward-reference to avoid needing
}; // 'delete ptr' before the node is complete.

struct node
{
std::unique_ptr<node, node_deleter> next;
};

void node_deleter::operator()(node* ptr)
{
delete ptr;
}

Mind you, I have not tested it in MSVC, and probably you should try to upgrade to VC 11, or file a bug to Microsoft if even VC 11 cannot compile your original code.

Pimpl with unique_ptr : Why do I have to move definition of constructor of interface to .cpp?

The constructor needs to destroy the class members, in the case that it exits by exception.

I don't think that making the constructor noexcept would help, though maybe it should.



Related Topics



Leave a reply



Submit