Search code examples
c++c++20type-traitsc++-concepts

Concept as_same identity fail


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:

  1. Why my concept didn't match for std::vector<int>::iterator and int* ?
  2. 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 ? Thx @cpplearner

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.


Solution

  • 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>;