I want to get rid of all the unholy enable_if
s in my templates and replace them with C++20 concepts, however there's barely any info on concepts and the syntax changes with just about any source I read.
Here's a function that takes two iterators of any container with MyClass
values:
template <class IteratorType, typename = std::enable_if<std::is_same<
typename std::iterator_traits<IteratorType>::value_type,
MyClass
>::value, void>>
void myFunction( IteratorType begin, IteratorType end ) {}
I know this function could be converted using concepts but I just can't find good leads to start with.
In computer programming, an iterator is an object that enables a programmer to traverse a container, particularly lists. Various types of iterators are often provided via a container's interface.
An iterator is an object that points to an element inside a container. Like a pointer, an iterator can be used to access the element it points to and can be moved through the content of the container. Each container in the C++ Standard Library provides its own iterator, as well as some methods to retrieve it.
There are various kinds of iterators: input, output, forward, bidirectional, and random-access.
To fit in with the C++20 Ranges ecosystem:
template <std::input_iterator I, std::sentinel_for<I> S>
requires std::same_as<std::iter_value_t<I>, MyClass>
constexpr void myFunction(I begin, S end)
{
// ...
}
Probably not the easiest to understand reference, but the normative source of information for concepts is the available standard draft. Where a concept definition is specified grammatically as
1 A concept is a template that defines constraints on its template arguments.
concept-definition: concept concept-name = constraint-expression ; concept-name: identifier
It's pretty much just like a bool variable template constant, but it's defined with the concept keyword. So to translate your condition directly to a concept is essentially this
template<typename T>
concept MyClassIter = std::is_same_v<
MyClass,
typename std::iterator_traits<T>::value_type
>;
With the concept in hand, we can use it as a type constraint on a template's type parameter, thus transforming your template into this
template <MyClassIter IteratorType>
void myFunction( IteratorType begin, IteratorType end ) {}
If the constraint is not satisfied for a type, this overload is discarded. Not satisfied in this context also includes substitution failures. So it's the same condition you had originally.
Live example
The most straightforward translation would be
template <typename IteratorType>
requires std::same_as<typename std::iterator_traits<IteratorType>::value_type, MyClass>
void myFunction(IteratorType begin, IteratorType end) {}
See:
Godbolt example
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With