Home > Blockchain >  How to constraint a template to be iterable ranges using concepts?
How to constraint a template to be iterable ranges using concepts?

Time:11-02

Say I have some template function that returns the median of some iterable object passed into it.

Something like:

template<typename T>
decltype(auto) find_median_sorted(T begin)
{
  // some code here
}

Now I want to make sure I constrained T to always be iterable. I am trying to learn how to use concepts in C , so is there some way I can use concept here to make sure T is iterable?

I'm sure there are other ways to check if it is iterable, but is this a wrong use case for concepts? I also came across this post here related to element iterable, but I am not sure how this applies to my case.

CodePudding user response:

I am trying to learn how to use concepts in C , so is there some way I can use concept here to make sure T is iterable?

You can have standard concept std::ranges::range from <ranges> header here. With that your function will look like:

#include <ranges>  // std::ranges::range

template<std::ranges::range T>
decltype(auto) find_median_sorted(T const& container) {
    // some code here
}

To restrict the ranges to be only for random access iterable ranges, you can use either std::ranges::random_access_range again from <ranges> header

#include <ranges>  // std::ranges::random_access_range

template<std::ranges::random_access_range T>
decltype(auto) find_median_sorted(T const& container) {
    // some code here
}

or via iterator concept std::random_access_iterator as follows:

#include <iterator> // std::random_access_iterator 

template<typename  T>
decltype(auto) find_median_sorted(T const& container)
requires std::random_access_iterator<std::ranges::iterator_t<T>>
{
    // some code here
}

CodePudding user response:

Edit: The original code in the question looked a lot more like iterators, hence why an iterator-based answer. As it stands, the range-based answer is a better fit for OP's request.

An important distinction between "old-school" iterators and their modern interpretation is that the sentinel (aka end iterator) does not necessarily have to be the same type as the begin one.

So you should be using 2 separate constraints. One for the iterator itself, and another one for the sentinel, using std::sentinel_for:

As for the iterator itself, you will want to use the loosest iterator concept that allows you to perform the work you want to do. Ideally std::input_iterator if possible.

Applying the constraint is simply a matter of replacing typename in the template by the appropriate concept:

#include <iterator>

template<std::input_iterator I, std::sentinel_for<I> S>
auto find_median_sorted(I begin, S end){
  // some code here
}
  • Related