vendredi 19 avril 2019

unique_ptr and forward declaration: the proper way to code a factory function

Learned smart ptrs recently, I'm trying to write a factory function that returns unique_ptrs. Having read several articles about putting the time of creation along with explicitly defined ctor and dtor in the same cpp file, I thought I could do this:

// factory.hpp

struct Foo;

std::unique_ptr<Foo> create();

// foo.hpp

struct Foo {
    Foo();
    ~Foo();
    Foo(const Foo &);
    Foo(Foo &&);
};

std::unique_ptr<Foo> create() {
    return make_unique<Foo>();
}

#include "factory.hpp"


int main() {
    auto r = create();
    return 0;
}

But I'm getting THE incomplete type error. Then after several hours of web searching and experiments, I realize that I can't even do this:

Here's the classic unique_ptr Pimpl idiom.

// A.hpp

struct B;

struct A {
    Foo();
    ~Foo();
    unique_ptr<B> b;
};

// A.cpp

struct B {};

Foo::Foo() = default;

Foo::~Foo() = default;


#include "A.hpp"


int main() {
    A a;   // this is fine since we are doing the Pimpl correctly.

    // Now, I can't do this.
    auto b = std::move(a.b);   <--- Can't do this.

    return 0;
}

For the sake of discussion, please ignore the fact that the std::move line makes no frigging sense. I got the same incomplete type error.

The above two cases are essentially the same. And after some searching, I guess I understand the reason behind the error, but I want some pointers (pun intended) and confirmation from you guys.

  1. It's UB to delete an incomplete type. That's why it's forbidden to create unique_ptrs w/ incomplete types using default deleters.
  2. If I use custom deleters, I should be able to do this.
  3. I'm guessing since I'm using default deleters in my case, I can't be done for some reason I'm not quite sure about.

Explicitly define create and destroy functions should do the trick. But to me, it's ugly. For one, default deleters will do in my case. For another, it seems to me I can't use lambda for the destroyer, as the type of lambda is known only to the compiler, and I can't do my factory function declaration with decltype.

So my questions are:

  1. What's the reason behind this failure?
  2. What's the proper way to write factory functions that returns unique_ptrs?

Please correct me if anything I've said is wrong. Any pointers would be appreciated.

Aucun commentaire:

Enregistrer un commentaire