Search code examples
c++templatescontainersspecializationduplication

Template specialization containers


I will open the question with a code sample:

 template <template <class, class> class Container>
    class Schedule {
        public:

         Schedule& print( std::ostream& os);

     private:
         Container<Course*, std::allocator<Course*> >  courses;
     };

     // implement funcion print:

     template <template <class, class> class Container>
     Schedule<Container>& Schedule<Container>::print(std::ostream& os){

         std::sort(courses.begin(), courses.end(), sor_con());
         std::for_each(courses.begin(), courses.end(), PrintContainer(os));
         return *this;
     }

     template<>
     Schedule<std::list>& Schedule<std::list>::print(std::ostream& os){

         courses.sort(sort_con());
         std::for_each(courses.begin(), courses.end(), PrintContainer(os));
         return *this;
     }

The Schedule class contains template classes (std::list / std::vector) only. Because the print function needs to use sort, I need use two different ways to implement this: std::sort from for std::vector and sort function of list container for std::list.

My code works, but I think I created unnecessary code duplication here. Is there a more efficient way to solve this problem?


Solution

  • You could either overload sort function for std::vector and std::list respectively:

    template<typename T, typename Compare>
    void sort(std::list<T> &list, Compare f)
    {
        list.sort(f);
    }
    
    template<typename T, typename Compare>
    void sort(std::vector<T> &vector, Compare f)
    {
        std::sort(vector.begin(), vector.end(), f);
    }
    

    Or use type_traits and tag_dispatching:

    #include <iostream>
    #include <list>
    #include <vector>
    #include <algorithm>
    #include <functional>
    
    struct vectorlike_tag { };
    struct listlike_tag   { };
    
    template <typename C> struct container_traits;
    
    template <typename T, typename A>
    struct container_traits<std::vector<T, A>> {
      typedef vectorlike_tag category;
    };
    
    template <typename T, typename A>
    struct container_traits<std::list<T, A>> {
      typedef listlike_tag category;
    };
    
    template <typename Container, typename Compare>
    void sort_helper(Container& c, Compare f, vectorlike_tag) {
      std::sort(c.begin(), c.end(), f);
    }
    
    template <typename Container, typename Compare>
    void sort_helper(Container& c, Compare f, listlike_tag) {
      c.sort(f);
    }
    
    template <typename Container, typename Compare>
    void sort_container(Container &c, Compare f) {
      sort_helper(c, f, typename container_traits<Container>::category());
    }
    
    template<class Container>
    void sort_container(Container &c)
    {
      sort_helper(c, std::less<typename Container::value_type>(), typename container_traits<Container>::category());
    }
    
    int main()
    {
      std::vector<int> v{ 4, 3, 7, 8, 9 };
      sort_container(v);
      for (auto e : v) std::cout << e << " ";
      std::cout << std::endl;
      std::list<int> lst{ 4, 3, 7, 8, 9 };
      sort_container(lst, std::greater<int>());
      for (auto e : lst) std::cout << e << " ";
      std::cout << std::endl;
      return 0;
    }