Home > Net >  No matching default constructor for static unique_ptr
No matching default constructor for static unique_ptr

Time:09-01

I'm trying to use my custom functions for unique_ptr deletion. But the compiler gives me errors. Somehow I suspect that this is because my deleter is not a class type. But I swear it worked before with functions, I used to plug in C-functions just fine. What is the real deal?

Demo

#include <cstdio>
#include <memory>
#include <utility>

using cTYPE = struct {
    int a;
};

void delete_wrapper(cTYPE* ptr)
{
    return free(ptr);
}

cTYPE* new_wrapper()
{
    return static_cast<cTYPE*>(malloc(sizeof(cTYPE)));
}

int main()
{
    auto foo = []() {
        using tmp_storage_type = std::unique_ptr<cTYPE, decltype(delete_wrapper)>;
        static tmp_storage_type obj;
        obj = tmp_storage_type{ new_wrapper(), delete_wrapper };
    };

    foo();    
}

Errors (amongst others, clang gives better errors here):

<source>:23:33: note: in instantiation of template class 'std::unique_ptr<cTYPE, void (cTYPE *)>' requested here
        static tmp_storage_type obj;
                                ^
<source>:24:15: error: no matching constructor for initialization of 'tmp_storage_type' (aka 'unique_ptr<cTYPE, void (cTYPE *)>')
        obj = tmp_storage_type{ new_wrapper(), delete_wrapper };

CodePudding user response:

You can't use a function type as template argument for the Deleter template parameter of std::unique_ptr. Use a function pointer instead:

using tmp_storage_type = std::unique_ptr<cTYPE, decltype(&delete_wrapper)>;

Because this would come with the potential pitfall that the value-initialized state of the deleter would be a null pointer, which causes undefined behavior when accidentally called, the default constructor of std::unique_ptr is however removed if a pointer type is used and therefore

static tmp_storage_type obj;

without initializing with a deleter will still not work.


It would be better to use a simple function object:

struct FreeDeleter {
    void operator()(void* p) const noexcept { std::free(p); }
};

using tmp_storage_type = std::unique_ptr<cTYPE, FreeDeleter>;

It also works for all types of pointers allocated with malloc and can be used with a default-constructed std::unique_ptr.


Also beware that using malloc/free this way only works because cTYPE and all of its subobjects happen to be of implicit-lifetime type. If that wasn't the case using malloc instead of new would result in undefined behavior sooner or later.


Also beware that malloc and free are not guaranteed to be imported into the global namespace scope with the includes you are using. First of all because these functions are declared in <cstdlib> which you don't include and secondly because even including that they may be declared only in std::. So you should qualify them with std::.


Also, writing using cTYPE = struct { is really just a weird way of writing struct cTYPE {, except that the former comes with additional restrictions with regards to what the class can contain and how linkage is applied to it.

CodePudding user response:

To add up to the other answer, another problem is represented by the line static tmp_storage_type obj;.

That line is erroneous not because of static, but just because the default constructor is not available, as per the explanation of overload #1 here. Indeed std::is_default_constructible<decltype(delete_wrapper)>::value is false, hence unique_ptr<>::unique_ptr() is SFINAEd out.

  • Related