c++c++11visual-studio-2012type-traitssize-type

Choosing between size_t and container::size_type in compile time


I was thinking to be more pedantic in choosing data type in a block of code where I would need to choose between size_type size_t in general or container::size_type for container types. My problem is that if I have the following block of code, I don't know how to do it. Can anybody there help?

template<typename some_container>
int func(some_container& input)
{
    //Some code...
    //...
    decltype(input.size()) variable_x; //Choose this if defined,
    size_t                 variable_x; //otherwise choose this
    //... Some more code...
}

In this case some_container may be a custom container and does not provide size() function. What led me to this thinking was reading the difference between size_t and container::size_type at size_t vs container::size_type. I also read Determine if a type is an STL container at compile time, but the approach feels a bit heavy-handed for my situation.


Solution

  • Following is one way to determine if a class contains a type (e.g. size_type) or not:

    template <typename T> 
    struct Has_size_type
    {
      typedef char (&yes)[2];
    
      template <typename C> static yes test(typename C::size_type*);
      template <typename> static char test(...);
    
      static const bool value = sizeof(test<T>(0)) == sizeof(yes);
    };
    

    And following is the way to choose between 2 types:

    template<bool> struct Bool;
    template<typename T, typename = Bool<true> >
    struct Set { typedef size_type type; };
    template<typename T>
    struct Set<T,Bool<Has_size_type<T>::value> > { typedef typename T::size_type type; };
    

    Edit start: Here is another simpler approach:

    template<typename T>
    struct void_ { typedef void type; };
    
    template<typename T, typename = void>
    struct Set 
    { typedef size_type type; };
    
    template<typename T>
    struct Set<T,typename void_<typename T::size_type>::type>
    { typedef typename T::size_type type; };
    

    Edit end.

    So finally, use as below:

    template<typename some_container>
    int func(some_container& input)
    {
      typedef typename Set<some_container>::type type;
    }
    

    So now type is either size_type or some_container::size_type, if it has that.