views:

259

answers:

2

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++?

+2  A: 

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);
}
UncleBens
I had to add specializations for T* and const T*, but it worked, I think. Thanks.
Mark Ruzon
+1  A: 

UncleBens has used SFINAE, but there is actually simpler:

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

Now, if you want to use it with a class you control, the easiest way is:

struct MyClass { typedef float value_type; };

BOOST_MPL_ASSERT((boost::is_same< MyClass::value_type,
                                  typename value_type<MyClass>::type >));

And if you want to use for a class you do not control, you still have specialization:

struct ThirdPartyClass {};

template <>
struct value_type<ThirdPartyClass> { typedef int type; }

If you try to use value_type for a class that does not have an inner typedef and for which no specialization is available, it's a compilation error (and a message you are not likely to understand at first glance...)

Matthieu M.
But since float::value_type does not exist, it will fail to compile for the key case of POD types.
Mark Ruzon
Like `std::iterator_traits` is specialized for pointers, you need to specialize your structure for each type that you cannot modify, yes.
Matthieu M.