16

Is it possible to specialize an Iterator template parameter by its value_type?

I have a function with the following prototype.

template<typename InputIterator>
void f(InputIterator first, InputIterator last);

And I want to handle specially if InputIterator::value_type is SomeSpecificType.

niboshi
  • 1,448
  • 3
  • 12
  • 20
  • I'm not sure what you want. Is this for use with a template specialization? Is there a reason you can't just use the above and reference InputIterator::value_type in the body? – Jonathan Sternberg Jun 08 '11 at 03:19
  • Hm, you could do all sorts of typetraiting stuff, but if you already want an _input_iterator with a specific value type, wouldn't that mean that whatever you get is a derived class with base class `std::iterator`? So you wouldn't need any templating at all, just one fixed function. – Kerrek SB Jun 08 '11 at 03:22
  • @Kerrek: no it doesn't mean that. Iterators do not share a common base class. Case in point: pointers are iterators. – R. Martinho Fernandes Jun 08 '11 at 03:36
  • @Kerrek: `std::iterator<...>` is not polymorphic and doesn't implement any useful operations. – aschepler Jun 08 '11 at 03:37
  • You're right, sorry for that. One more thing learned about iterators :-) Luc's answer below nails it nicely. – Kerrek SB Jun 08 '11 at 04:01

5 Answers5

12

You can use some intermediate structs to get the partial template specialisation that you need. Something like this should do the trick

template<typename T, typename V>
struct f_impl
{
  static void f( T first, T last ) {...}; //Default version
};

template<typename T>
struct f_impl<T, SomeSpecificType>
{
   static void f(T first,T last) {...}; //Specialisation
};

template<typename InputIterator> void f(InputIterator first, InputIterator last)
{
  f_impl<
      InputIterator,
      typename std::iterator_traits<InputIterator>::value_type
  >::f(first,last);
};
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
  • 3
    `typename std::iterator_traits::value_type` would be better than `typename InputIterator::value_type`. – aschepler Jun 08 '11 at 03:34
8

Using SFINAE, assuming enable_if[_c] and is_same are either from Boost or <type_traits> (and are appropriately qualified with either boost:: or std:: respectively):

template<typename InputIterator>
typename enable_if<
    !is_same<
        typename std::iterator_traits<InputIterator>::value_type,
        SomeSpecificType
    >::value
>::type
f(InputIterator first, InputIterator last)
{
    // Default implementation.
}

template<typename InputIterator>
typename enable_if<
    is_same<
        typename std::iterator_traits<InputIterator>::value_type,
        SomeSpecificType
    >::value
>::type
f(InputIterator first, InputIterator last)
{
    // Special case
}

In the Boost case, use boost::enable_if_c for something similar to the above. You can use boost::enable_if and get rid of the ::value but then must also use e.g. boost::disable_if.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • 1
    Note that `boost::enable_if` wants a metafunction whereas `std::enable_if` wants a `bool` value. The proper Boost corollary in this context is `boost::enable_if_c`. – ildjarn Jun 08 '11 at 03:29
  • D'oh; that's what I get for reading the code and only skimming the surrounding text. :-P – ildjarn Jun 08 '11 at 03:39
0

How about:

template<typename T>
typename std::enable_if<std::is_same<typename T::value_type, SomeType>::value, void>::type
f(T first, T second);
Gene Bushuyev
  • 5,512
  • 20
  • 19
  • 1
    @Martinho Fernandes: you can constrain the second version of `f` too to remove the ambiguity – Daniel Jun 08 '11 at 03:27
  • @Martino: you can have as many overloads as you need as long as you correctly `enable_if` each one. – Gene Bushuyev Jun 08 '11 at 03:30
  • @Dani: I see no simple way to add that constraint, without a wrapper function call like in @Michael Anderson's answer. – aschepler Jun 08 '11 at 03:30
  • @Martinho: I have a lot of operator overloading in AXE, all done with SFINAE facilities. You wouldn't be able to do it any differently, when you need to preserve perfect forwarding. – Gene Bushuyev Jun 08 '11 at 03:33
0

This will work. This type of specialization only works with structs, so I can't do this with a function.

template <typename InputIterator, typename ValueType = typename InputIterator::value_type>
struct foobar
{
    static void invoke(InputIterator first, InputIterator second)
    {
        // ...
    }
};

template <typename InputIterator>
struct foobar<InputIterator, SomeSpecificType>
{
    static void invoke(InputIterator first, InputIterator second)
    {
        // ...
    }
};

This shouldn't require you to set the type. It should be inferred automatically.

Jonathan Sternberg
  • 6,421
  • 7
  • 39
  • 58
0

Call me naive, but why wouldn't the following suffice?

struct MyType; // the only type I want

#include <iterator>
typedef std::iterator<std::input_iterator_tag, MyType> MyIt;

void f(const MyIt & begin, const MyIt & end)
{
   /* ... */
}

OK, forget that above, that was nonsense. Here's a way to do it, which is just Luc's correct answer from above, for C++0x:

#include <vector>
#include <iterator>
#include <type_traits>

// "int" is our desired iterator value type, "void" is f's return type.
template <typename It>
typename std::enable_if<std::is_same<int, typename std::iterator_traits<It>::value_type>::value, void>::type
f(const It & begin, const It & end) { /* your function here */ }

int main()
{
  std::vector<double> x;
  std::vector<int> y;

  //f(x.cbegin(), x.cend()); // error
  f(y.cbegin(), y.cend());   // works
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Now attempt to do anything at all useful with `begin` or `end` in this specialized `f`. – aschepler Jun 08 '11 at 03:41
  • Right, I see. The inheritance from std::iterator is only for typetraiting purposes... never mind then. The real answer would probably involve some typetraiting work after all. – Kerrek SB Jun 08 '11 at 03:45