mercredi 26 octobre 2022

Idiomatic C++11 for delegating template specialisations to a default implementation

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