Как применить ограничения между шаблоном и параметрами шаблона в C++17

У меня есть следующий код, который компилируется (GCC7, C++17):

template<typename T>
struct NDArrayHostAllocatorNew
{
    static T* allocate(std::size_t size) {
        return new T[size];
    }

    static void deallocate(const T* ptr){
        delete [] ptr;
    }
};

template<typename T, template<typename TT> typename Alloc>
class NDArrayHost
{
public:
    typedef Alloc<T> allocator_type;
    NDArrayHost(std::size_t size);
private:
    T* m_data;
};

template<typename T, template<typename TT> typename Alloc>
NDArrayHost<T, Alloc>::NDArrayHost(std::size_t size)
{
    m_data = allocator_type::allocate(size);
}

Вот мои вопросы:

  • Если я использую T вместо TT Я получаю ошибку, что один T тени другой. Хорошо, честно, но в моем случае я хочу T быть таким же с TT, Как я могу обеспечить это? Я думаю, я мог бы использовать std::enable_if а также std::is_same каким-то образом? Но в этом случае код станет слишком волосатым. Есть ли менее волосатое решение для этого?

  • Я едва вижу код с параметрами шаблона шаблона. Я делаю что-то, что не считается хорошей практикой?

  • Мне не очень нравится синтаксис этого решения. Есть ли лучший способ сделать то же самое, но с более чистым / простым кодом?

  • Хотя код с параметрами шаблона шаблона очень уродлив, совершенно очевидно, что этот код понимает: он просто позволяет пользователю указать собственный механизм выделения памяти для NDArrayHost объекты. Хотя это подходит для совершенно другого / отдельного вопроса: если вы считаете, что я подхожу к проблеме совершенно неправильно, не стесняйтесь указывать мне на лучшее решение (пока оно не очень сложное, как Thrust,

1 ответ

Решение

По пути стоит объявить базовый шаблон с точки зрения типов T а также Alloc а затем только предоставить юридическую частичную специализацию.

#include <cstddef>
#include <memory>

template<typename T>
struct NDArrayHostAllocatorNew
{
    static T* allocate(std::size_t size) {
        return new T[size];
    }

    static void deallocate(const T* ptr){
        delete [] ptr;
    }
};

/*
 * declare the base template in terms of T and allocator
 */
template<typename T, typename Alloc>
class NDArrayHost;

/*
 * only provide legal specialisations
 */
template<class T, template<class> class Alloc>
class NDArrayHost<T, Alloc<T>>
{
public:
    typedef Alloc<T> allocator_type;
    NDArrayHost(std::size_t size);
private:
    T* m_data;
};

template<class T, template<class> class Alloc>
NDArrayHost<T, Alloc<T>>::NDArrayHost(std::size_t size)
{
    m_data = allocator_type::allocate(size);
}

Мы можем, если захотим, добавить специализацию для предоставления диагностики, если T не совпадают:

/* specifically disallow illegal specialisations */
template<class T, class U, template<class> class Alloc>
class NDArrayHost<T, Alloc<U>>
{
    static_assert(std::is_same<T, U>(), "meh");
};

Тестирование...

int main()
{
    NDArrayHost<int, NDArrayHostAllocatorNew<int>> h(10);

    // fails with static assert
    // NDArrayHost<int, NDArrayHostAllocatorNew<double>> h2(10);
}
Другие вопросы по тегам