Retrieving iterator value type

In your templated function you might want to use a value type stored in the container to which you got an iterator type as a template parameter. The idiomatic way to achieve this would be to use value_type member of the iterator:


template<class Iterator>
void fun(Iterator it) {
  using ValueType = Iterator::value_type;
  ...
}

This will work on all properly implemented iterators, but say you want to support lazy implementations of iterators as well. We can deduce what the containing type behind this iterator is by inspecting what type is returned when the iterator is dereferenced:


template<class Iterator>
void fun(Iterator it) {
  using ValueType = 
    std::remove_reference_t<decltype(*it)>;
  ...
}

(Removing the reference is not strictly necessary, but it makes the type more versatile. For example, you can't create std::set<ValueType&>.) This ValueType can only be used in the body of the function. What if you want to return that type or something that includes it?


template<class Iterator>
auto fun(Iterator it)
  -> std::remove_reference_t<decltype(*it)>
{
  using ValueType = 
    std::remove_reference_t<decltype(*it)>;
  ...
}

That will work, but now the same resulting type needs to be independently derived twice. This is not only irritating to look at, it also has functional setbacks: it uses an existing argument to derive a type. This method therefore can't be used to declare function arguments themselves.

You might as well templetise that in a straightforward way too and just ask directly what the type should be. Seems like the simplest solution:


template<class Iterator, class ValueType>
ValueType fun(Iterator it, ValueType v)
{
  //using ValueType = ... already have it
  ...
}

I didn't like that for a number of reasons. Most importantly it can only work if one of the arguments provided is of ValueType. With Iterator type provided the function should have all the information needed, why would there be a need to collect it as an independent piece?


template<
  class Iterator, 
  class ValueType = 
    std::remove_reference_t<
      decltype(*std::declval<Iterator>())>>
ValueType fun (Iterator it, ValueType val)
{
  ...
}

ValueType is predefined as whatever the iterator returns when it is dereferenced. It is only deduced once. You can now return it, receive it as an argument, and work with it in function body.

Next we can make this easier to reuse:


template<class Iterator> 
using ValueType = 
  std::remove_reference_t<
    decltype(*std::declval<Iterator>())>;

This way you can use it anywhere as simply as this:


ValueType<Iterator> fun (
   Iterator it,
   ValueType<Iterator> val)
{
   return *it+val;
}

This construct is now useful beyond iterators because it opens possibilities to write generic code. To illustrate, standard iterators offer value_type from which you can learn what type is stored in container, but standard smart pointers have element_type. I don't know why they chose different name for basically the same idea. It is hindering generic code writing efforts even when only using std, let alone all other libraries that your project needs to work with. The only limit to this typedef is that the type is dereferencable using *. We can now write generic code that uses underlying type of standard compliant iterators, badly implemented iterators, smart pointers, std::optional, and custom classes that envelop types, you name it. Even C-style arrays and raw pointers!


Previous: When to use nested namespaces in C++
Next: Notes from porting to std::string_view