C++ Logo

std-proposals

Advanced search

std::algorithm over Iterable types

From: Ryan Kelly <ryantk98_at_[hidden]>
Date: Mon, 6 Jul 2020 13:34:12 -0500
Howdy all! I'm wishing to float a proposal idea.

My idea is to allow the std::algorithm library to work over Iterable types
(aka the std:: containers and beyond). It might be more reasonable to start
with a subset of the algorithms library. Two basic examples to now
demonstrate are std::reverse and std::max_element.

Define reverse like:

template<class Iterable> inline constexpr
std::enable_if_t<is_iterable_v<Iterable>, Iterable&>
reverse(Iterable& c)
{
  return std::reverse(c.begin(), c.end());
}

Now:

std::vector<int> vi{0, 1, 2, 3, 4, 5};
std::reverse(v.begin(), v.end()); // old way
std::reverse(v); // new way -- much more concise!!

Similarly for std::max_element, define:

template<class Iterable> inline constexpr
std::enable_if_t<is_iterable_v<Iterable>, iterator<Iterable>>
max_element(Iterable& c)
{
  return std::max_element(c.begin(), c.end());
}

Now:

std::vector<int> vi{0, 1, 2, 3, 4, 5};
auto iter = std::max_element(vi.begin(), vi.end()); // old way
auto iter = std::max_element(vi); // new way. Neat!

You'll notice in my definitions I use a trait called 'is_iterable_v'. This
is a concept defined the pre-C++20 way. It tests for the existence of
begin() & end() methods of the type. It tests that begin() and end() are
inequality (!=) comparable. And it tests that begin yields a derefenceable
type. Here's an example:

template<class T>
decltype(std::declval<T&>().begin() != std::declval<T&>().end(),
void(),
++std::declval<decltype(std::begin(std::declval<T&>()))&>(),
void(*std::begin(std::declval<T&>())),
std::true_type{})
iterable_impl(int);
template<class T> std::false_type iterable_impl(...);
template<class T>
using is_iterable = decltype(iterable_impl<T>(0));

template<class T> inline constexpr bool is_iterable_v =
is_iterable<T>::value;

There's a few more useful traits to include for such an extension. Another
is the iterator trait:

template<class T>
using iterator = decltype(std::declval<T&>().begin());

And a trait that yields the subtype of a collection:

template<class T>
using deref = decltype(*std::declval<T>());

template<class Container>
using subtype = std::remove_reference_t<deref<iterator<Container>>>;

You can find some of my prototype implementations in this file:
https://github.com/rthomaskelly/basic_utils/blob/master/iterable_algorithms.hpp

I think this is rather feasible and useful. I'd like to know if I should
proceed with a proposal.

Received on 2020-07-06 13:37:38