实现 C++20 bidirectional_iterator 概念

Implement C++20 bidirectional_iterator concept

Forward_iterator 编译正常!

#include <concepts>
#include <iterator>
// #include <iterator_concept>

using namespace std;

template<typename T>
// struct my_iterator : std::forward_iterator_tag{
struct my_iterator : std::bidirectional_iterator_tag {
   T& operator*()const{
      return *pointer;
   }
   
   my_iterator& operator++(){
      ++pointer;
      return *this;
   }
   my_iterator operator++(int){
      auto copy = *this;
      ++pointer;
      return copy;
   }
   
//    my_iterator& operator--(){
//       --pointer;
//       return *this;
//    }
//    my_iterator operator--(int){
//       auto copy = *this;
//       --pointer;
//       return copy;
//    }
   
   using difference_type = ptrdiff_t;
   using value_type = T;
   
   // auto operator<=>(const my_iterator&) const = default;
   bool operator==(const my_iterator& other)const{
      return pointer == other.pointer;
   }
   bool operator!=(const my_iterator& other)const{
      return pointer != other.pointer;
   }
   
private:
   T*pointer;
};
// template<class T> constexpr bool always_false = false;
// static_assert(always_false<std::iter_reference_t<my_iterator<int>>>, "asd");
// typedef typename std::iter_reference_t<my_iterator<int>>::something_made_up X;
// static_assert(std::weakly_incrementable<my_iterator<int>>);
// static_assert(std::equality_comparable<my_iterator<int>>);
static_assert(std::forward_iterator<my_iterator<int>>);
// static_assert(std::bidirectional_iterator<my_iterator<int>>);

int main()
{
   my_iterator<int> i1;
}

live snippet godbolt

但双向没有。我试过 g++ v10 和 clang++ v11。两者给出几乎相同的编译时错误。

#include <concepts>
#include <iterator>

using namespace std;

template<typename T>
// struct my_iterator : std::forward_iterator_tag{
struct my_iterator : std::bidirectional_iterator_tag {
   T& operator*()const{
      return *pointer;
   }
   
   my_iterator& operator++(){
      ++pointer;
      return *this;
   }
   my_iterator operator++(int){
      auto copy = *this;
      ++pointer;
      return copy;
   }
   
   my_iterator& operator--(){
      --pointer;
      return *this;
   }
   my_iterator operator--(int){
      auto copy = *this;
      --pointer;
      return copy;
   }
   
   using difference_type = ptrdiff_t;
   using value_type = T;
   
   // auto operator<=>(const my_iterator&) const = default;  // Why this does not work
   bool operator==(const my_iterator& other)const{
      return pointer == other.pointer;
   }
   bool operator!=(const my_iterator& other)const{
      return pointer != other.pointer;
   }
   
private:
   T*pointer;
};
// template<class T> constexpr bool always_false = false;
// static_assert(always_false<std::iter_reference_t<my_iterator<int>>>, "asd");
// typedef typename std::iter_reference_t<my_iterator<int>>::something_made_up X;
// static_assert(std::weakly_incrementable<my_iterator<int>>);
// static_assert(std::equality_comparable<my_iterator<int>>);
static_assert(std::forward_iterator<my_iterator<int>>);
// static_assert(std::bidirectional_iterator<my_iterator<int>>);

int main()
{
   my_iterator<int> i1;
}

live

In file included from <source>:2:
In file included from /opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/iterator:61:
In file included from /opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/stl_iterator_base_types.h:71:
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:409:52: error: ambiguous partial specializations of '__cat<my_iterator<int>>'
      using iterator_category = typename __detail::__cat<_Iterator>::type;
                                                   ^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/stl_iterator_base_types.h:178:14: note: in instantiation of template class 'std::__iterator_traits<my_iterator<int>, void>' requested here
    : public __iterator_traits<_Iterator> { };
             ^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:182:71: note: in instantiation of template class 'std::iterator_traits<my_iterator<int>>' requested here
        = __is_base_of(__iterator_traits<_Iter, void>, iterator_traits<_Iter>);
                                                                             ^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:182:4: note: while substituting template arguments into constraint expression here
        = __is_base_of(__iterator_traits<_Iter, void>, iterator_traits<_Iter>);
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:189:16: note: while checking the satisfaction of concept '__primary_traits_iter<my_iterator<int>>' requested here
      requires __primary_traits_iter<_Iter>
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:189:16: note: while substituting template arguments into constraint expression here
      requires __primary_traits_iter<_Iter>
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:195:7: note: (skipping 10 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
      using __iter_traits = typename __iter_traits_impl<_Iter, _Tp>::type;
      ^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:564:30: note: while checking the satisfaction of concept 'input_or_output_iterator<my_iterator<int>>' requested here
    concept input_iterator = input_or_output_iterator<_Iter>
                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:564:30: note: while substituting template arguments into constraint expression here
    concept input_iterator = input_or_output_iterator<_Iter>
                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:575:32: note: while checking the satisfaction of concept 'input_iterator<my_iterator<int>>' requested here
    concept forward_iterator = input_iterator<_Iter>
                               ^~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:575:32: note: while substituting template arguments into constraint expression here
    concept forward_iterator = input_iterator<_Iter>
                               ^~~~~~~~~~~~~~~~~~~~~
<source>:52:15: note: while checking the satisfaction of concept 'forward_iterator<my_iterator<int>>' requested here
static_assert(std::forward_iterator<my_iterator<int>>);
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:369:14: note: partial specialization matches [with _Iter = my_iterator<int>]
      struct __cat<_Iter>
             ^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:375:14: note: partial specialization matches [with _Iter = my_iterator<int>]
      struct __cat<_Iter>
             ^
<source>:52:1: error: static_assert failed
static_assert(std::forward_iterator<my_iterator<int>>);
^                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:52:20: note: because 'my_iterator<int>' does not satisfy 'forward_iterator'
static_assert(std::forward_iterator<my_iterator<int>>);
                   ^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:575:32: note: because 'my_iterator<int>' does not satisfy 'input_iterator'
    concept forward_iterator = input_iterator<_Iter>
                               ^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:564:30: note: because 'my_iterator<int>' does not satisfy 'input_or_output_iterator'
    concept input_iterator = input_or_output_iterator<_Iter>
                             ^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:544:5: note: because 'my_iterator<int>' does not satisfy 'weakly_incrementable'
        && weakly_incrementable<_Iter>;
           ^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:198:7: note: because 'typename iter_difference_t<_Iter>' would be invalid: no type named 'difference_type' in 'std::iterator_traits<my_iterator<int>>'
      using __iter_diff_t = typename
      ^
2 errors generated.
Compiler returned: 1

x86-64 clang 11.0.0
1

更多问题:

  1. 为什么auto operator<=>(const my_iterator&) const = default;不提供==!=? 可能是这样,但 static_assert(std::forward_iterator<my_iterator<int>>); 希望 operator== 明确。

  2. 有没有办法明确地说这个结构应该满足概念的要求和约束,比如

    template<typename T>
    struct my_iterator implements forward_iterator {};
    

    目前我需要用static_assert(std::forward_iterator<my_iterator<int>>);检查一下。

这个:

struct my_iterator : std::bidirectional_iterator_tag {

不是您为迭代器指示迭代器类别的方式。您不从标签类型继承,而是将其作为成员类型别名。

像这样:

struct iterator {
    using iterator_category = std::bidirectional_iterator_tag;
};

虽然这实际上不是必需的。即使没有这个标签(并且 definitely 没有继承),这个迭代器也应该算作双向迭代器,因为你满足了其他要求。它实际上是一个libstdc++ bug,它在那个时候没有编译。


Why auto operator<=>(const my_iterator&) const = default; does not provide == and !=?

确实如此。只是您从不可比较的类型(迭代器标记)继承,因此默认比较被定义为已删除。

Is there a way to explicitly say this struct should meet the requirements and constraints of the concept

没有。