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?
#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.