2

This is not a duplicate of questions on template constructors or even on calling inherited template constructors.

It is specifically about calling the inherited constructor in a subclass of an class instance(?) of the unique_ptr<...,...> template.

The problem

To make the code easier to understand, I'm using using in this example:

using B = std::unique_ptr<int *, decltype(&::free)>;

class int_ptr : public B {
    int_ptr(int *b) : B(b, &::free) { };
};

but compilation fails:

In constructor 'int_ptr::int_ptr(int*)':
error: no matching function for call to 
    'std::unique_ptr<int*, void (*)(void*) throw ()>::unique_ptr(int*&, void (*)(void*) throw ())'

int_ptr(int *b) : B(b, &::free) { };
                              ^

The only possible cause of lack of function matching I can think of is the presence of throw () but I'm not sure what to do about that, or if it even is a problem. Possibly unique_ptr is no-throw.

Otherwise the no-matching function is precisely what I would expect to have matched.


The reason

I don't want to keep specifying the destructor every time I declare an instance of a unique_ptr. There is a good alternative in this answer https://stackoverflow.com/a/16615285/2332068, but I'm interested to know what is wrong with my attempt.

Of course, in real life it is not an int* I'm trying to wrap in unique_ptr, but some opaque (but not void*) pointer.

My intent is to have all these pointers from a C API correctly freed when the scope exits.

Perhaps I could constrain these pointers into a class/struct with a destructor but I can't see that it would save much.

Result

With the tip from @RSahu, I have come up with this, unique_dptr to avoid the need to keep specifying the destructor, but as an alternative to overriding the template delete function in the std namespace:

void free_int(int* p) {
  delete p;
}

template<typename T, void (*D)(T*)>
class unique_dptr : public std::unique_ptr<T, decltype(D)> {
    public: unique_dptr(T* t) : std::unique_ptr<T, decltype(D)>(t, D) { };
};

using int_ptr = unique_dptr<int, ::free_int>;
int_ptr i(new int(2));
Community
  • 1
  • 1
Sam Liddicott
  • 1,265
  • 12
  • 24
  • 4
    I suspect you need to use `using B = std::unique_ptr;`. – R Sahu Oct 29 '15 at 15:18
  • @RSahu That's the answer. – Angew is no longer proud of SO Oct 29 '15 at 15:21
  • Interesting. But only for the `using` clause. I had previously had all instances of `int*` replaced with `int` but got: `error: invalid conversion from 'int' to 'std::unique_ptr::pointer {aka int*}' [-fpermissive]` I guess this is because the template takes the type but the template instance constructor takes the pointer. - thanks! Please make it an answer so I can mark you as the answerer! – Sam Liddicott Oct 29 '15 at 15:22

1 Answers1

5

std::unique<int> is meant to be smart pointer for ints, i.e. a replacement for int*.

When you use std::unique<int*>, the pointer needs to be int**.

Instead of

using B = std::unique_ptr<int *, decltype(&::free)>;

use

using B = std::unique_ptr<int, decltype(&::free)>;

Working code (Thanks, @CompuChip): http://ideone.com/ul29vr.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 2
    Proof: http://ideone.com/ul29vr. (By the way, you might want to make that constructor more accessible than private.) – CompuChip Oct 29 '15 at 15:24
  • I hadn't realise that the template takes a type, but that the generated constructor instead takes a pointer to the same type. – Sam Liddicott Oct 29 '15 at 15:25