Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make a valid C++ alias template for std::unique_ptr

I want to make an alias template for std::unique_ptr that supplies my own deleter function.

unique_ptr has both a scalar and an array implementation, they're defined like this:

template <class T, class D = default_delete<T>>
class unique_ptr // scalar

template <class T, class D>
class unique_ptr<T[], D> // array

I'm running into trouble trying to override both the scalar and array versions of unique_ptr. It's easy to make an alias for just one version, like this:

template<class T>
struct Deleter {
    void operator()(T* ptr) { delete ptr; }
};

template<class T>
using my_unique_ptr = std::unique_ptr<T Deleter<T>>;

But when I try to add a second alias, like this:

template<class T>
struct ArrayDeleter {
    void operator()(T* ptr) { delete [] ptr; }
};
template<class T>
using my_unique_ptr = std::unique_ptr<T[], ArrayDeleter<T>>;

... I end up with compiler errors because "my_unique_ptr" is ambiguous.

My question is: How can I create a single aliased name that works for both the array and scalar versions of unique_ptr?

like image 977
Dominic Dos Santos Avatar asked Mar 02 '17 21:03

Dominic Dos Santos


2 Answers

You appear to be trying to specialize a using declaration. You may not.

template<class T>
struct my_unique_ptr_helper {
  using type = std::unique_ptr<T, Deleter<T>>;
};
template<class T>
struct my_unique_ptr_helper<T[]> {
  using type = std::unique_ptr<T[], ArrayDeleter<T>>;
};

template<class T>
using my_unique_ptr = typename my_unique_ptr_helper<T>::type;

Now this has disadvantages in that it blocks deduction rather completely.

We can fix this by moving the specialization elsewhere.

template<class T>
struct Deleter {
  void operator()(T* ptr) const {
    delete ptr;
  }
};
template<class T>
struct ArrayDeleter {
  void operator()(T* ptr) const {
    delete[] ptr;
  }
};
template<class T>
struct Deleter<T[]>:ArrayDeleter<T> {}; // inheritance

now:

template<class T>
using my_unique_ptr = std::unique_ptr<T, Deleter<T>>;

is simpler, and may permit more deduction of T.

Of course, this is all pretty pointless, but I presume your real Deleter isn't the same as std::default_delete.

like image 119
Yakk - Adam Nevraumont Avatar answered Sep 28 '22 14:09

Yakk - Adam Nevraumont


You should show us your ArrayDeleter but... are you sure that you can't solve your problem with a unique using and std::conditional?

I mean, something like

template <typename T>
using my_unique_ptr = std::unique_ptr<T,
         typename std::conditional<std::is_array<T>::value,
                                   ArrayDeleter<T>,
                                   Deleter<T>>::type>;

--- EDIT ---

The OP say

I've edited the question to include an example ArrayDeleter implementation

Not sure (I do a lot of errors with template array specializations) but I suppose that should work if you can modify ArrayDeleter as follows

template <typename>
struct ArrayDeleter;

template <typename T>
struct ArrayDeleter<T[]>
 { void operator()(T* ptr) { delete [] ptr; } };
like image 23
max66 Avatar answered Sep 28 '22 14:09

max66