Search code examples
c++iteratorc++20c++-concepts

How to make a function accept all type of iterators, but specialising only one type?


As per How to make constructor accept all type of iterators? I am trying to have a function accept iterators from different container types. Unlike that question, i want those containers to have only the specific type of elements. However, if i use the following code, i lose the ability to control the element type:

template<std::input_iterator ITER>
void f(ITER start, ITER end)
{
    for(auto it=start; it!=end; ++it)
    {
        static_assert(std::is_arithmetic<decltype(*it)>::value, "Not arithmetic");
    }
}

In here static_assert is not a very poor solution, because the error that i get is anything, but trivial to understand.

Let's imagine my main as

int main()
{
    std::vector<int> v_good{{1, 2, 3, 4, 5}};
    std::vector<std::string> v_bad{{std::string("hello"), std::string("world")}};
    std::list<int> l_good{{1, 2, 3, 4, 5}};
    
    f(v_good.begin(), v_good.end());
    f(l_good.begin(), l_good.end());
    f(v_bad.begin(), v_bad.end()); // at this point i want to get a compilation error
    
    return 0;
}

Solution

  • You can constrain the value_type of the iterator (std::iter_value_t) to be an arithmetic type by

    template<std::input_iterator ITER>
      requires std::is_arithmetic_v<std::iter_value_t<ITER>>
    void f(ITER start, ITER end)
    {
        for (auto it=start; it!=end; ++it)
        {
           // ...
        }
    }
    

    In C++20, the type returned by a range's end() (i.e., the sentinel type) can be different from its begin(), so you may also need

    template<std::input_iterator ITER, std::sentinel_for<ITER> SENT>
      requires std::is_arithmetic_v<std::iter_value_t<ITER>>
    void f(ITER start, SENT end)
    {
        for(auto it=start; it!=end; ++it)
        {
           // ... 
        }
    }