Rishiraj Surti Rishiraj Surti - 17 days ago 7
C++ Question

Why use iterator_traits in a template function instead of just using another template type argument?

Referring, for example, to this snippet from cplusplus.com:

template <class InputIterator, class T>
typename iterator_traits<InputIterator>::difference_type
count(InputIterator first, InputIterator last, const T& val)
{
typename iterator_traits<InputIterator>::difference_type ret = 0;
while (first!=last) {
if (*first == val)
++ret;
++first;
}
return ret;
}


The question is why use
iterator_traits
in this context rather than taking in another template argument as shown here:

template <class InputIterator, class T, class DiffType>
DiffType count(InputIterator first, InputIterator last, const T& val)
{
DiffType ret = 0;
while (first!=last) {
if (*first == val)
++ret;
++first;
}
return ret;
}

Answer

The suggestion you proposed in the comments - having the function take in another template argument - will not work as you intended. Here's the code you've suggested:

template <class InputIterator, class T, typename DiffType>
DiffType count(InputIterator first, InputIterator last, const T& val)
{
    DiffType ret = 0;
    while (first!=last) {
        if (*first == val)
            ++ret;
        ++first;
    }
    return ret;
}

The problem with this code is that this no longer compiles:

std::vector<int> v = /* ... */;
auto numElems = count(v.begin(), v.end(), 137); // <--- Error!

The issue here is that in order to invoke a function template, every template argument either has to be deducible from the argument types or explicitly specified by the caller. Here, the type DiffType can't be deduced from the argument types (the InputIterator and T types can be inferred from the two arguments, but from the signature alone there's no context), so this call would fail with a compiler error. The use of std::iterator_traits here is a general template pattern for extracting information about an iterator from the iterator type itself.