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
}