Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::allocator_traits defaults with allocator that has more than one template parameter

std::allocator_traits works its magic automatically when I provide an STL-style container with an allocator that has a single template parameter but it doesn't when I provide an STL-style container with an allocator that has two template parameters but is otherwise similar.

What do I need to do to tell std::allocator_traits how to interact with an allocator that has more than one template parameter? Is it possible to get std::allocator_traits to provide reasonable defaults in this case?

As an example, if I take the simple allocator Howard Hinnant provides in Allocator Boilerplate and feed it to a std::vector<> then all is well. If I add a dummy int parameter to the allocator template (and make slight modifications as needed) then I get compiler errors because the compiler couldn't find rebind, among other things.

Here's that description in code:

http://coliru.stacked-crooked.com/a/173c57264137a351

If I have to specialize std::allocator_traits myself in this case, is there a way to still get the defaults?

like image 389
Praxeolitic Avatar asked Mar 14 '23 14:03

Praxeolitic


1 Answers

The Standard only provides a default rebind for allocators with multiple template type parameters:

17.6.3.5 Allocator requirements [allocator.requirements]

3 Note A: The member class template rebind in the table above is effectively a typedef template. [ Note: In general, if the name Allocator is bound to SomeAllocator<T>, then Allocator::rebind<U>::other is the same type as SomeAllocator<U>, where SomeAllocator<T>::value_type is T and SomeAllocator<U>:: value_type is U. — end note ] If Allocator is a class template instantiation of the form SomeAllocator<T, Args>, where Args is zero or more type arguments, and Allocator does not supply a rebind member template, the standard allocator_traits template uses SomeAllocator<U, Args> > in place of Allocator:: rebind<U>::other by default. For allocator types that are not template instantiations of the above form, no default is provided.

Since you have a non-type (int) parameter, there is no default provided. The fix is simple: just add your own rebind to your allocator.

template<class T, int I>
class allocator_w_int
{
    // as before

    template<class U>
    struct rebind { using other = allocator_w_int<U, I>; };    
};

Live Example

As to the rationale of allowing for allocators of the form Allocator<T, Args...> but not for allocators of the form Alloc<T, Ns...>, one can only guess, but then it would also lead to the plethora of Alloc<T, Args.., Ns...> etc. etc.. This is why template-metaprogramming libraries (such as Boost.MPL) always wrap their non-type parameters N of type T inside things like integral_constant<T, N>. This would also be a route for you, by defining

template<class T, class Arg>
class allocator_w_int; // leave undefined

template<int N>
using int_ = std::integral_constant<int, N>;

template<class T, int I>
class allocator_w_int<T, int_<I>>
{
    // replace all occurances of I, J --> int_<I>, int_<J>
};

Live Example

like image 127
TemplateRex Avatar answered Apr 26 '23 19:04

TemplateRex