Search code examples
c++templatesiteratorgeneric-programming

Can C++'s value_type be extended from iterator_traits to all types?


I would like to create a construct similar to std::iterator_traits::value_type that can work seamlessly for all types using the same syntax. Imagine we have the following:

template <typename T>
struct value_type {
  typedef T type;
};

#define VALUE_TYPE(T) typename value_type<T >::type

This will work for POD types. I can specialize it for my own class:

struct MyClass {
  typedef float value_type;
};

template <>
struct value_type<MyClass> {
  typedef MyClass::value_type type;
};

though I would prefer to avoid extra value_type instantiations in an ideal world.

The problem is with STL iterators. I need a specialization that gets me to the iterator hierarchy. This fails because the compiler chooses the base case:

template <>
struct value_type<std::_Iterator_base_aux> {  // MSVC implementation
  typedef value_type type;
};

Choosing a class higher up the hierarchy (_Iterator_with_base would be most natural because that is where value_type is defined) fails because it requires specifying all the iterator traits as template arguments.

Is what I'm trying to do even possible in C++?


Solution

  • You can use SFINAE to detect the presence of the value_type typedef. No need to specialize for individual types (which might not be possible, since you'd be relying entirely on internal implementation details).

    #include <vector>
    
    template <class T>
    struct has_value_type
    {
        typedef char true_type;
        typedef char false_type[2];
    
        //template not available if there's no nested value_type in U's scope
        template <class U>
        static true_type test(typename U::value_type* ); 
    
        //fallback
        template <class U>
        static false_type& test(...);
    
        //tests which overload of test is chosen for T
        static const bool value = sizeof(test<T>(0)) == sizeof(true_type);
    };
    
    template <class T, bool b>
    struct value_type_impl;
    
    template <class T>
    struct value_type_impl<T, false> //if T doesn't define value_type
    {
        typedef T type;
    };
    
    template <class T>
    struct value_type_impl<T, true> //if T defines value_type
    {
        typedef typename T::value_type type;
    };
    
    template <class T>
    struct value_type: value_type_impl<T, has_value_type<T>::value>
    {
    };
    
    struct MyClass {
      typedef float value_type;
    };
    
    template <class T>
    int foo(T )
    {
        return typename value_type<T>::type();
    }
    
    int main()
    {
        foo(MyClass());
        std::vector<int> vec;
        foo(vec.begin());
        foo(10);
    }