Question

I want to write template function void foo() which takes 2 iterators or pointers.

I want to have a specialized version for random_access_iterator. I try to write like below:

// generic version
template<class Iter, typename std::iterator_traits<Iter>::iterator_category>
void foo(Iter first, Iter last);

// Specialized version for random_access_iterator
template<class RandomIter>
void foo<RandomIter, std::random_access_iterator_tag>(RandomIter first, RandomIter last)
{
}

It gives error: illegal use of explicit template arguments

Please let me know what is correct way of defining this. Thanks

Was it helpful?

Solution 2

Based on "tag dispatching" pattern, I've written a solution to my question:

// Using "= delete" on un-supported categories to get concise compile-time error information
template<typename Iter> void _foo(Iter first, Iter last, std::bidirectional_iterator_tag) = delete;

template<typename RandomIter>
void _foo(RandomIter first, RandomIter last, std::random_access_iterator_tag)
{ ... }

template<class Iter>
void foo(Iter first, Iter last)
{
    typename std::iterator_traits<Iter>::iterator_category category;
    _foo(first, last, category);
}

It works now. Please leave comment if it has bug or need improvement. Thanks.

OTHER TIPS

You can't partially specialize function templates. Partial specialization is only available to class templates. What you can do is to have just one function template which delegates to static members of suitably specialized class templates, e.g.

template <typename Iter,
          typename = typename std::iterator_traits<Iter>::iterator_category>
struct foo_impl {
    static void call(Iter first, Iter last);
};

template <typename Iter>
struct foo_impl<Iter, std::random_acces_iterator_tag> {
    static void call(Iter first, Iter last);
};

templlate <typename Iter>
void foo(Iter first, last) {
    foo_impl<Iter>::call(first, last);
}

Alternatively you can use partial ordering. In that case there are two function templates one taking a general iterator category as argument and another one taking a specific category as argument. The category or a pointer to it is passed as additional argument. I think this requires a forwarding function, too (I'm normally using the approach using partial specializations of a class template).

As a complement of previous answers with ways to achieve the same result using newer C++ standard versions.

C++17

You can use if constexpr:

#include <iterator>
#include <type_traits>

template<class Iter>
void foo(Iter first, Iter last)
{
    using Cat = typename std::iterator_traits<Iter>::iterator_category;
    if constexpr (std::is_same_v<Cat, std::random_access_iterator_tag>) {
        // random access iterator
    }
    else if constexpr (std::is_same_v<Cat, std::bidirectional_iterator_tag>) {
        // bidirectional iterator
    }
    else if constexpr (std::is_same_v<Cat, std::forward_iterator_tag>) {
        // forward iterator
    }
    else if constexpr (std::is_same_v<Cat, std::input_iterator_tag>) {
        // input iterator
    }
}

C++20

You can overload the function and discriminate between each case using concepts:

#include <iterator>

template<std::random_access_iterator Iter>
void foo(Iter first, Iter last)
{
    // random access iterator
}

template<std::bidirectional_iterator Iter>
void foo(Iter first, Iter last)
{
    // bidirectional iterator
}

template<std::forward_iterator Iter>
void foo(Iter first, Iter last)
{
    // forward iterator
}

template<std::input_iterator Iter>
void foo(Iter first, Iter last)
{
    // input iterator
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top