I'm making a struct Box<T>
that handles some data. The specifics are unimportant.
An important note however is that Box<T>
can store a pointer, but it might not. So both Box<int>
and Box<int *>
are valid. Obviously, if we own Box.data
, we're going to need to delete
data if it is a pointer type. Here's a solution I came up with that works in C++11:
template <typename T> struct BoxTraits;
template <typename T> struct Box {
using traits_t = BoxTraits<T>;
T data;
~Box() = default; // not required, I know
T get_data() { return traits_t::get_data(this); }
};
template <typename T> struct Box<T *> {
using traits_t = BoxTraits<T *>;
T *data;
~Box() { delete data; }
T *get_data() { return traits_t::get_data(this); }
};
template <typename T> struct BoxTraits {
static T get_data(Box<T> *const box) { return box->data; }
};
Box::get_data
is here to illustrate an issue with this design pattern. For every single method I want to add to Box
, I need to add some boiler plate in each specialisation. Note that I would also need a Box<T *const>
specialisation.
This seems like quite a rubbish solution. In C++14, I could use if constexpr
with a is_ptr<T>
trait and only have to write extra code in the methods that need specialising... Is there any way I can do this is in C++11?
This solution is shorter, cleaner and works for Box<U *const>
!
template <typename T> struct is_ptr { static const bool value = false; };
template <typename U> struct is_ptr<U *> { static const bool value = true; };
template <typename U> struct is_ptr<U *const> {
static const bool value = true;
};
template <typename T> struct Box {
T data;
~Box() {
if constexpr (is_ptr<T>::value) {
delete data;
}
}
T get_data() { return data; }
};
Aucun commentaire:
Enregistrer un commentaire