自定义容器类型与视图一起使用的要求

requirements for custom container type to use with views

我开始玩 std::ranges 并想了解视图的真正工作原理。所以我尝试写自己的容器和迭代器类型,想在视图中使用它。

但似乎缺少某些东西,但编译器只告诉我视图中没有 begin() 方法,但没有说明原因。

示例:

#include <iostream>
#include <array>
#include <ranges>

class MyFixedContainer;
class MyIterator
{
    MyFixedContainer* ptr;
    unsigned int offset;
public:
    MyIterator( MyFixedContainer* ptr_, unsigned int offset_ ): ptr{ ptr_},offset{offset_}{}

    bool operator==( MyIterator& other ) const
    {   
        return ( ptr == other.ptr )&& ( offset == other.offset );
    }   

    bool operator!=( MyIterator& other ) const
    {   
        return !(*this == other);
    }   

    MyIterator operator++()
    {   
        offset++;
        return *this;
    }   

    MyIterator operator++(int)
    {   
        MyIterator tmp = *this;
        offset++;
        return tmp;
    }   

    int operator*() const;
};

class MyFixedContainer
{
    std::array<int,4> arr={5,6,7,8};
public:
    auto begin() { return MyIterator{ this, 0 }; }
    auto end() { return MyIterator{ this, 4}; }

    int Get( int offset ) const
    {
        return arr[ offset ];
    }
};

int MyIterator::operator*() const
{
    return ptr->Get( offset );
}

int main()
{
    MyFixedContainer c;

    // Container type itself works:
    for ( int i: c )
    {
        std::cout << i << std::endl;
    }

    // Try to use with std::ranges
    auto even = [] (int i) { return 0 == i % 2; };

    auto y = std::views::filter(c, even);
    auto b = y.begin(); // << error message
}

编译为

main.cpp:90:16: error: 'struct std::ranges::views::__adaptor::_RangeAdaptorClosurestd::ranges::views::__adaptor::_RangeAdaptor<_Callable::operator()<{MyFixedContainer&, main()::<lambda(int)>&}>::<lambda(_Range&&)> >' has no member named 'begin' 90 | auto b = y.begin();

https://godbolt.org/z/doW76j

MyIterator 没有建模 std::input_or_output_iterator 因为:

  • 需要默认可构造。
  • std::iter_difference_t<MyIterator>必须有效,并且
  • pre-increment 运算符必须 return 引用。

MyIterator 不是 std::sentinel_for<MyIterator, MyIterator> 因为它的运算符 ==!= 使用引用而不是 const 引用。

MyIterator不满足std::input_iterator,需要std::iter_value_t才有效

修复以上所有问题:

#include <iostream>
#include <array>
#include <ranges>

class MyFixedContainer;
class MyIterator
{
    MyFixedContainer* ptr;
    unsigned int offset;
public:
    using difference_type = int;
    using value_type = int;
    
    MyIterator() = default;
    
    MyIterator( MyFixedContainer* ptr_, unsigned int offset_ ): ptr{ ptr_},offset{offset_}{}

    bool operator==( MyIterator const & other ) const
    {   
        return ( ptr == other.ptr )&& ( offset == other.offset );
    }   

    bool operator!=( MyIterator const & other ) const
    {   
        return !(*this == other);
    }   

    MyIterator &operator++()
    {   
        offset++;
        return *this;
    }   

    MyIterator operator++(int)
    {   
        MyIterator tmp = *this;
        offset++;
        return tmp;
    }   

    int operator*() const;
};

class MyFixedContainer
{
    std::array<int,4> arr={5,6,7,8};
public:
    auto begin() { return MyIterator{ this, 0 }; }
    auto end()   { return MyIterator{ this, 4}; }

    int Get( int offset ) const
    {
        return arr[ offset ];
    }
};

int MyIterator::operator*() const
{
    return ptr->Get( offset );
}

int main()
{
    MyFixedContainer c;

    // Container type itself works:
    for ( int i: c )
    {
        std::cout << i << std::endl;
    }

    // Try to use with std::ranges
    auto even = [] (int i) { return 0 == i % 2; };

    static_assert(std::input_or_output_iterator<MyIterator>);
    static_assert(std::ranges::input_range<MyFixedContainer>);
    
    auto y = c | std::views::filter(even);
    
    auto b = y.begin(); // << OK
}

如果您 static_assert 您的 container/iterator 必须建模的每个概念,错误消息会更加清晰。