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>
.
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.
Well, after getting very helpful answers and comments from @TemplateRex and @Casey, I finally came up with the following improved solution:
The helper class template is_allocator
checks whether the given template argument (the potentially Allocator) has an embedded type value_type
, whether it implements a member allocate(n)
and whether it implements deallocate(ptr, n)
, where ptr
is of type result_type of allocate
.
A few requirements are still not checked, as you can see here: Requirements
(Edited): applying further improvements after @Casey's comments:
template <class T>
struct __has_allocate
{
private:
template <class U> static std::false_type test(...);
template <class U> static std::true_type test(decltype(std::declval<U>().allocate(0)));
public:
enum { value = decltype(test<T>(0))::value };
};
template <class T>
struct __has_value_type
{
private:
template <class U> static std::false_type test(...);
template <class U> static std::true_type test(typename U::value_type*);
public:
enum { value = decltype(test<T>(0))::value };
};
template <class T, bool HasAllocate = has_allocate<T>::value>
struct __has_deallocate
{
private:
typedef decltype(std::declval<T>().allocate(0)) pointer;
template <class Alloc, class Pointer>
static auto
test(Alloc&& a, Pointer&& p)
-> decltype(a.deallocate(p,0), std::true_type());
template <class Alloc, class Pointer>
static auto
test(const Alloc& a, Pointer&& p)
-> std::false_type;
public:
enum { value = decltype(test<T>(std::declval<T>(), std::declval<pointer>()))::value };
};
template <class T>
struct __has_deallocate<T, false>
{
enum { value = false };
};
template <class T>
struct is_allocator
{
enum { value = __has_value_type<T>::value
and __has_allocate<T>::value
and __has_deallocate<T>::value
};
};
// Test:
static_assert(is_allocator<int>::value == false, "");
static_assert(is_allocator<std::allocator<int>>::value == true, "");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With