Changing value_type of a given STL container

前端 未结 3 1573
盖世英雄少女心
盖世英雄少女心 2021-01-06 17:26

Suppose, I\'ve a STL container type (not object), say vector. Now it\'s value_type is A, so I want to change it to <

相关标签:
3条回答
  • 2021-01-06 18:18

    Just stumbled over this as I was trying to essentially solve the same problem. It can even be made to work without relying on the rebind type that is specific to std::allocator – the only requirement is that the rebound value type is the first template parameter of the respective classes. This is the case for all relevant STL classes (std::vector, std::set, std::list etc. as well as for example std::less and std::allocator).

    A pre-C++11 solution would look like this:

    template <class Container, class NewType>
    struct rebind;
    
    template <class ValueType, template <class> class Container, class NewType>
    struct rebind<Container<ValueType>, NewType>
    {
      typedef Container<NewType> type;
    };
    
    template <class ValueType, class A, template <class, class> class Container, class NewType>
    struct rebind<Container<ValueType, A>, NewType>
    {
      typedef Container<NewType, typename rebind<A, ValueType>::type> type;
    };
    
    template <class ValueType, class A, class B, template <class, class, class> class Container, class NewType>
    struct rebind<Container<ValueType, A, B>, NewType>
    {
      typedef Container<NewType, typename rebind<A, ValueType>::type, typename rebind<B, ValueType>::type> type;
    };
    
    // Continue for more parameters (A, B, C, ...)
    

    C++11 makes it a bit easier:

    template <class Container, class NewType>
    struct rebind;
    
    template <class ValueType, class... Args, template <class...> class Container, class NewType>
    struct rebind<Container<ValueType, Args...>, NewType>
    {
      typedef Container<NewType, typename rebind<Args, NewType>::type...> type;
    };
    

    In order to support std::array, the following can be added:

    template <class ValueType, std::size_t N, template <class, std::size_t> class Container, class NewType>
    struct rebind<Container<ValueType, N>, NewType>
    {
      typedef Container<NewType, N> type;
    };
    

    The result can be used with just about any STL type:

    #include <iostream>
    #include <typeinfo>
    #include <vector>
    #include <set>
    #include <deque>
    #include <queue>
    #include <list>
    #include <array>
    
    #include "rebind.h"
    
    // Make it all a bit more compact
    #define REBIND_DEMO(container, new_type)                \
      do {                                                  \
        container test;                                     \
        rebind<decltype(test), new_type>::type test2;       \
        std::cout << typeid(test).name() << "\n";           \
        std::cout << typeid(test2).name() << "\n";          \
      } while (0)
    
    int main()
    {
      REBIND_DEMO(std::set<float>, double);
      REBIND_DEMO(std::list<float>, double);
      REBIND_DEMO(std::deque<float>, double);
      REBIND_DEMO(std::queue<float>, double);
      typedef std::array<float, 4> TestArray;
      REBIND_DEMO(TestArray, double);
      REBIND_DEMO(std::unordered_set<float>, double);
    
      return 0;
    }
    

    Running this and piping the output through c++filt -t on a Linux system gives you

    std::set<float, std::less<float>, std::allocator<float> >
    std::set<double, std::less<double>, std::allocator<double> >
    std::list<float, std::allocator<float> >
    std::list<double, std::allocator<double> >
    std::deque<float, std::allocator<float> >
    std::deque<double, std::allocator<double> >
    std::queue<float, std::deque<float, std::allocator<float> > >
    std::queue<double, std::deque<double, std::allocator<double> > >
    std::array<float, 4ul>
    std::array<double, 4ul>
    std::unordered_set<float, std::hash<float>, std::equal_to<float>, std::allocator<float> >
    std::unordered_set<double, std::hash<double>, std::equal_to<double>, std::allocator<double> >
    
    0 讨论(0)
  • 2021-01-06 18:19

    You're referring (I believe) to the Policy Clone idiom, using rebind

    0 讨论(0)
  • 2021-01-06 18:26

    You might try specializing with template template parameters.

    #include <vector>
    #include <list>
    #include <deque>
    #include <string>
    
    template <class T, class NewType>
    struct rebind_sequence_container;
    
    template <class ValueT, class Alloc, template <class, class> class Container, class NewType>
    struct rebind_sequence_container<Container<ValueT, Alloc>, NewType >
    {
         typedef Container<NewType, typename Alloc::template rebind<NewType>::other > type;
    };
    
    template <class Container, class NewType>
    void test(const NewType& n)
    {
        typename rebind_sequence_container<Container, NewType>::type c;
        c.push_back(n);
    }
    
    int main()
    {
        std::string s;
        test<std::vector<int> >(s);
        test<std::list<int> >(s);
        test<std::deque<int> >(s);
    }
    

    However, containers might not have those two template parameters.

    Also, in container adapters and associative containers, not just the allocator would need replacing (underlying container in adapters, predicate in std::set). OTOH, their usage is so different from sequence containers that it is hard to imagine a template that works with any container type.

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