简体   繁体   中英

How to enforce constraints between template and template template parameters in C++17

I have the following code that compiles (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);
}

Here are my questions:

  • If I use T instead of TT I get an error that one T shadows the other. OK fair enough, but in my case, I want T to be the same with TT . How can I enforce that? I guess I could use std::enable_if and std::is_same somehow? But in that case the code will become too hairy. Is there any less hairy solution for this?

  • I barely see code with template template parameters. Am I doing something that is not considered a good practice?

  • I don't really like the syntax of this solution. Is there a better way to do the same but with cleaner/simpler code?

  • While the code with the template template parameters is big ugly, it is quite obvious to understand what this code does: It just allows the user to specify his/her own mechanism to allocate memory for NDArrayHost objects. Although this qualifies for a whole different/separate question: if you think that I am approaching the problem completely wrong, feel free to point me to a better solution (as long as is not very complex like Thrust .

On way is to declare the base template in terms of types T and Alloc and then only provide a legal partial specialisation.

#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);
}

We could if we liked, add a specialisation to provide a diagnostic if the T's don't match:

/* 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");
};

Testing...

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

    // fails with static assert
    // NDArrayHost<int, NDArrayHostAllocatorNew<double>> h2(10);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM