I have a nice type traits that check if a type T
is an iterator:
I try to do the same but with concept. My first attempt was:
template<typename T>
concept isIterator = requires(T a) {
{ typename std::iterator_traits<T>::difference_type{} } -> std::same_as<typename std::iterator_traits<T>::difference_type>;
{ typename std::iterator_traits<T>::pointer{} } -> std::same_as<typename std::iterator_traits<T>::pointer>;
{ typename std::iterator_traits<T>::value_type{} } -> std::same_as<typename std::iterator_traits<T>::value_type>;
{ typename std::iterator_traits<T>::iterator_category{} } -> std::same_as<typename std::iterator_traits<T>::iterator_category>;
// this line trigger the assert for
// static_assert(is_iterator_v<std::vector<int>::iterator>);
// static_assert(is_iterator_v<int*>);
{ typename std::iterator_traits<T>::reference{} } -> std::same_as<typename std::iterator_traits<T>::reference>;
};
// default case
template <class T>
struct is_iterator : std::false_type
{
};
// specialization
template <isIterator It>
struct is_iterator<It> : std::true_type
{
};
template <class T>
constexpr bool is_iterator_v = is_iterator<T>::value;
Demo : https://wandbox.org/permlink/shrJpZPIlS0pXKak
But it fails on:
static_assert(is_iterator_v<std::vector<int>::iterator>);
static_assert(is_iterator_v<int*>);
And passes on:
static_assert(is_iterator_v<std::vector<int>::const_iterator>);
static_assert(is_iterator_v<std::list<double>::const_iterator>);
static_assert(!is_iterator_v<std::list<double>>);
static_assert(!is_iterator_v<int>);
I have 2 questions:
std::vector<int>::iterator
and int*
?static_assert(is_iterator_v<std::vector<int>::const_iterator>);
works but a reference is not default constructible, so how can it pass the { typename std::iterator_traits<T>::reference{} }
requirement ?Note : I know that I can just do:
template<typename T>
concept isIterator = requires(T a) {
typename std::iterator_traits<T>::difference_type;
typename std::iterator_traits<T>::pointer;
typename std::iterator_traits<T>::value_type;
typename std::iterator_traits<T>::iterator_category;
typename std::iterator_traits<T>::reference;
};
And it works like a charm, but I would like to understand why my first attempt fail.
It fails with std::vector<int>::iterator
and int*
because:
std::iterator_traits<int*>::reference == int&
std::iterator_traits<const int*>::reference == int&
int&{}
does not compile (where const int&{}
does)So { typename std::iterator_traits<T>::reference{} }
fails for these types. It can be fixed with :
{ std::declval<typename std::iterator_traits<T>::reference>() } -> std::same_as<typename std::iterator_traits<T>::reference>;