c++ template class; function with arbitrary container type, how to define it?

前端 未结 3 649
名媛妹妹
名媛妹妹 2020-12-04 18:25

Okay, simple template question. Say I define my template class something like this:

template
class foo {
public:
    foo(T const& first         


        
3条回答
  •  刺人心
    刺人心 (楼主)
    2020-12-04 19:07

    Here's the latest and expanded version of this answer and significant improvement over answer by Sabastian.

    The idea is to define all traits of STL containers. Unfortunately, this gets tricky very fast and fortunately lot of people have worked on tuning this code. These traits are reusable so just copy and past below code in file called type_utils.hpp (feel free to change these names):

    //put this in type_utils.hpp 
    #ifndef commn_utils_type_utils_hpp
    #define commn_utils_type_utils_hpp
    
    #include 
    #include 
    
    namespace common_utils { namespace type_utils {
        //from: https://raw.githubusercontent.com/louisdx/cxx-prettyprint/master/prettyprint.hpp
        //also see https://gist.github.com/louisdx/1076849
        namespace detail
        {
            // SFINAE type trait to detect whether T::const_iterator exists.
    
            struct sfinae_base
            {
                using yes = char;
                using no  = yes[2];
            };
    
            template 
            struct has_const_iterator : private sfinae_base
            {
            private:
                template  static yes & test(typename C::const_iterator*);
                template  static no  & test(...);
            public:
                static const bool value = sizeof(test(nullptr)) == sizeof(yes);
                using type =  T;
    
                void dummy(); //for GCC to supress -Wctor-dtor-privacy
            };
    
            template 
            struct has_begin_end : private sfinae_base
            {
            private:
                template 
                static yes & f(typename std::enable_if<
                    std::is_same(&C::begin)),
                                 typename C::const_iterator(C::*)() const>::value>::type *);
    
                template  static no & f(...);
    
                template 
                static yes & g(typename std::enable_if<
                    std::is_same(&C::end)),
                                 typename C::const_iterator(C::*)() const>::value, void>::type*);
    
                template  static no & g(...);
    
            public:
                static bool const beg_value = sizeof(f(nullptr)) == sizeof(yes);
                static bool const end_value = sizeof(g(nullptr)) == sizeof(yes);
    
                void dummy(); //for GCC to supress -Wctor-dtor-privacy
            };
    
        }  // namespace detail
    
        // Basic is_container template; specialize to derive from std::true_type for all desired container types
    
        template 
        struct is_container : public std::integral_constant::value &&
                                                            detail::has_begin_end::beg_value  &&
                                                            detail::has_begin_end::end_value> { };
    
        template 
        struct is_container : std::true_type { };
    
        template 
        struct is_container : std::false_type { };
    
        template 
        struct is_container> : std::true_type { };
    
        template 
        struct is_container> : std::true_type { };
    
        template 
        struct is_container> : std::true_type { };
    
    }}  //namespace
    #endif
    

    Now you can use these traits to make sure our code only accepts container types. For example, you can implement append function that appends one vector to another like this:

    #include "type_utils.hpp"
    
    template
    static typename std::enable_if::value, void>::type
    append(Container& to, const Container& from)
    {
        using std::begin;
        using std::end;
        to.insert(end(to), begin(from), end(from));
    }
    

    Notice that I'm using begin() and end() from std namespace just to be sure we have iterator behavior. For more explanation see my blog post.

提交回复
热议问题