简体   繁体   English

将 std::ranges 算法与自定义容器和迭代器一起使用

[英]Using std::ranges algorithms with custom containers and iterators

I have the following simplified code representing a range of integers that I want to use with various std algorithms.我有以下简化代码,表示我想与各种 std 算法一起使用的整数范围。 I am trying to update my code to use C++20's ranges versions of the algorithms so I can delete all of the begin() and end() calls.我正在尝试更新我的代码以使用算法的 C++20 范围版本,以便我可以删除所有begin()end()调用。 In the below code, std::any_of works with my container and iterator, but std::ranges::any_of does not.在下面的代码中, std::any_of与我的容器和迭代器一起使用,但std::ranges::any_of不使用。

#include <iostream>
#include <algorithm>

class Number_Iterator {
    public:
        using iterator_category = std::input_iterator_tag;
        using value_type = int;
        using difference_type = int;
        using pointer = int*;
        using reference = int&;

        Number_Iterator(int start) noexcept : value(start) {}
        Number_Iterator& operator++() noexcept { ++value; return *this; }
        bool operator==(const Number_Iterator& other) const noexcept = default;
        int operator*() const noexcept { return value; }

    private:
        int value;
};

class Numbers {
    public:
        Numbers(int begin, int end) noexcept : begin_value(begin), end_value(end) {}
        Number_Iterator begin() const noexcept { return {begin_value}; }
        Number_Iterator end() const noexcept { return {end_value}; }

    private:
        int begin_value;
        int end_value;
};

int main() {
    const auto set = Numbers(1, 10);
    const auto multiple_of_three = [](const auto n) { return n % 3 == 0; };

    // Compiles and runs correctly
    if(std::any_of(set.begin(), set.end(), multiple_of_three)) {
        std::cout << "Contains multiple of three.\n";
    }

    // Does not compile
    if(std::ranges::any_of(set, multiple_of_three)) {
        std::cout << "Contains multiple of three.\n";
    }

    return 0;
}

When I try to compile the above code, I get the following error messages from Visual Studio 2019 (16.11.15) with the flag /std:c++20 :当我尝试编译上述代码时,我从 Visual Studio 2019 (16.11.15) 收到以下带有/std:c++20标志的错误消息:

Source.cpp(42,21): error C2672: 'operator __surrogate_func': no matching overloaded function found
Source.cpp(42,7): error C7602: 'std::ranges::_Any_of_fn::operator ()': the associated constraints are not satisfied
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\include\algorithm(1191): message : see declaration of 'std::ranges::_Any_of_fn::operator ()'

I have tried looking at the std::ranges::_Any_of_fn::operator() declaration, but I find myself more confused by that.我曾尝试查看std::ranges::_Any_of_fn::operator()声明,但我发现自己对此更加困惑。

What am I missing to get the std::ranges algorithms to work with my container?为了让std::ranges算法与我的容器一起工作,我缺少什么?


For the curious, what I'm actually iterating over are squares on a chess board, but those are represented by integers, so the difference from the above code isn't so great.出于好奇,我实际上迭代的是棋盘上的方格,但这些方格是用整数表示的,因此与上述代码的区别不是很大。

Apparently, the std::ranges algorithms require two more methods in the iterator: a default constructor and a post-increment operator (return value optional).显然, std::ranges算法在迭代器中需要另外两个方法:默认构造函数和后增量运算符(返回值可选)。 Adding these methods allows the code to compile and run correctly:添加这些方法可以使代码正确编译和运行:

Number_Iterator() noexcept : value(-1) {}
void operator++(int) noexcept { ++value; }

To use your range with any_of it must satisfy the input_range concept:要将您的范围与any_of一起使用,它必须满足input_range概念:

template< class T >
  concept input_range =
    ranges::range<T> && std::input_iterator<ranges::iterator_t<T>>;

Then via the input_iterator concept:然后通过input_iterator概念:

template<class I>
  concept input_iterator =
    std::input_or_output_iterator<I> &&
    std::indirectly_readable<I> &&
    requires { typename /*ITER_CONCEPT*/<I>; } &&
    std::derived_from</*ITER_CONCEPT*/<I>, std::input_iterator_tag>;

and via the input_or_output_iterator concept并通过input_or_output_iterator概念

template <class I>
concept input_or_output_iterator =
  requires(I i) {
    { *i } -> /*can-reference*/;
  } &&
  std::weakly_incrementable<I>;

you land in the weakly_incrementable concept:你进入了weakly_incrementable概念:

template<class I>
  concept weakly_incrementable =
    std::movable<I> &&
    requires(I i) {
      typename std::iter_difference_t<I>;
      requires /*is-signed-integer-like*/<std::iter_difference_t<I>>;
      { ++i } -> std::same_as<I&>;   // pre-increment
      i++;                           // post-increment
    };

in which you see that the iterator must have both the pre-increment and post-increment versions of operator++ .您可以在其中看到迭代器必须同时具有operator++的前增量和后增量版本。


The iterator must also be default constructible because std::ranges::end creates a sentinel:迭代器也必须是默认可构造的,因为std::ranges::end创建了一个标记:

template< class T >
    requires /* ... */
constexpr std::sentinel_for<ranges::iterator_t<T>> auto end( T&& t );

And sentinel_for还有sentinel_for

template<class S, class I>
  concept sentinel_for =
    std::semiregular<S> &&
    std::input_or_output_iterator<I> &&
    __WeaklyEqualityComparableWith<S, I>;

requires it to satisfy semiregular :要求它满足semiregular

template <class T>
concept semiregular = std::copyable<T> && std::default_initializable<T>;

But without being default constructible, this substitution will fail:但如果不是默认可构造的,这种替换将失败:

template < class T >
concept default_initializable = std::constructible_from<T> && requires { T{}; } && ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM