Why is allocator::rebind necessary when we have template template parameters?

前端 未结 4 2033
予麋鹿
予麋鹿 2020-11-27 17:07

Every allocator class must have an interface similar to the following:

template
class allocator
{
    ...
    template
    s         


        
相关标签:
4条回答
  • 2020-11-27 17:27

    Suppose you want to write a function taking all sorts of vectors.

    Then it is much more convenient being able to write

    template <class T, class A>
    void f (std::vector <T, A> vec) {
       // ...
    }
    

    than having to write

    template <class T, template <class> class A>
    void f (std::vector <T, A> vec) {
       // ...
    }
    

    In most of the cases, such a function does not care about the allocator anyway.

    Further note that allocators are not required to be a template. You could write separate classes for particular types that need to be allocated.

    An even more convenient way of designing allocators would probably have been

    struct MyAllocator { 
       template <class T>
       class Core {
          // allocator code here
       };
    };
    

    Then it would have been possible to write

    std::vector <int, MyAllocator> vec;
    

    rather than the somewhat misleading expression

    std::vector <int, MyAllocator<int> > vec;
    

    I am not sure whether the above MyAllocator is permitted to be used as an allocator after adding a rebind, i.e. whether the following is a valid allocator class:

    struct MyAllocator { 
       template <class T>
       class Core {
          // allocator code here
       };
    
       template <class T>
       struct rebind { using other=Core<T>; };
    };
    
    0 讨论(0)
  • 2020-11-27 17:33

    In your approach you are forcing the allocator to be a template with a single parameter, which might not be always the case. In many cases, allocators can be non-template, and the nested rebind can return the same type of the allocator. In other cases the allocator can have extra template arguments. This second case is the case of std::allocator<> which as all templates in the standard library is allowed to have extra template arguments as long as the implementation provides default values. Also note that the existence of rebind is optional in some cases, where allocator_traits can be used to obtain the rebound type.

    The standard actually mentions that the nested rebind is actually just a templated typedef:

    §17.6.3.5/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.

    0 讨论(0)
  • 2020-11-27 17:34

    A quoted text from Foundations of Algorithms in C++11, Volume 1, chap 4, p. 35 :

    template <typename T> 
    struct allocator 
    {  
       template <typename U>  
       using  rebind = allocator<U>; 
    }; 
    

    sample usage :

    allocator<int>::rebind<char> x;
    

    In The C++ Programming Language, 4th edition, section 34.4.1, p. 998, commenting the 'classical' rebind member in default allocator class :

    template<typename U>
         struct rebind { using other = allocator<U>;};
    

    Bjarne Stroustrup writes this:

    The curious rebind template is an archaic alias. It should have been:

    template<typename U>
    using other = allocator<U>;
    

    However, allocator was defined before such aliases were supported by C++.

    0 讨论(0)
  • 2020-11-27 17:42

    But why is this necessary?

    What if your allocator class has more than one template argument?

    That's pretty much it in terms of why it is generally discouraged to use template template arguments, in favor of using normal template arguments, even if it means a bit of redundancy at the instantiation site. In many cases (however, probably not for allocators), that argument might not always be a class template (e.g., a normal class with template member functions).

    You might find it convenient (within the implementation of the container class) to use a template template parameter just because it simplifies some of the internal syntax. However, if the user has a multi-argument class template as an allocator he wants to use, but you require the user to provide an allocator which is a single-argument class template, you will in effect force him to create a wrapper for almost any new context in which he must use that allocator. This not only unscalable, it can also become very inconvenient to do. And, at this point, that solution is far from being the "elegant and less redundant" solution you originally thought it would be. Say you had an allocator with two arguments, which of the following is the easiest for the user?

    std::vector<T, my_allocator<T,Arg2> > v1;
    
    std::vector<T, my_allocator_wrapper<Arg2>::template type > v2;
    

    You basically force the user to construct a lot of useless things (wrappers, template aliases, etc.) just to satisfy your implementation's demands. Requiring the author of a custom allocator class to supply a nested rebind template (which is just a trivial template alias) is far easier than all the contortions you require with the alternative approach.

    0 讨论(0)
提交回复
热议问题