Looking for an `is_allocator` Type Trait for Use in `enable_if`

c++11, enable-if, sfinae, template-meta-programming, templates

Solution

There is no such `is_allocator` trait in the Standard Library, but you can write one yourself:

#include <vector>
#include <utility>

template <class T>
class is_allocator
{
    typedef char yes;
    typedef long no;

    // you can extend this with many more checks on the allocator interface
    template <class C> static yes check( decltype(std::declval<C>().allocate(0)) );

    template <class C> static no  check(...);
public:
    enum { value = sizeof(check<T>(0)) == sizeof(yes) };
};

int main()
{
    std::vector<int> v { 1, 2 };
    using V = decltype(v)::value_type;
    using A = decltype(v)::allocator_type;
    static_assert(!is_allocator<V>::value, "");
    static_assert( is_allocator<A>::value, "");
}

Live Example.

The above code checks whether a type has a member function `allocate(size_type)` by calling that function inside a `decltype()` expression. If such a function exists, the `check<T>(0)` will select that overload in the `enum` expression and the `value` will become `true`. As a check, you can `static_assert` this on the template parameters of a `std::vector`.

Obviously, you could improve this approach by having a bunch of fine-grained traits `has_allocate`, `has_deallocate` and all the other essential member function that make up the entire Allocator requirements in the Standard. Once you have done that, you can define `is_allocator` as the logical and over all these fine-grained traits.

Problem

Is there a "sufficiently" reliable way to detect an allocator in a template parameter. That is, I need something like a `is_allocator` type trait which can be used in an `enable_if`: Suppose there is a class template future (with template parameter T): ``` // Default ctor with allocator template <class Alloc, class... Args class Enable = typename std::enable_if< is_allocator<Alloc>::value and std::is_constructible<T, Args...>::value >::type > future(const Alloc& a, Args&&... args) : _shared_value(std::allocate_shared<T>(a, std::forward<T>(args...)) { } // Default ctor (without allocator) template <class... Args class Enable = typename std::enable_if< std::is_constructible<T, Args...>::value >::type > future(Args&&... args) : _shared_value(std::make_shared<T>(std::forward<T>(args...)) { } ``` Here, `_shared_value` is a `std::shared_pointer<T>`.

Original source