【问题标题】:Cannot satisfy C++ range requirements custom container无法满足 C++ 范围要求自定义容器
【发布时间】:2021-11-14 04:12:23
【问题描述】:

我一直在尝试编写一个带有自己的迭代器的自定义容器,它可以用作一个范围和std::span。我是新手,所以请善待。

以下示例无法编译,因为我的容器类无法转换为 std::span


#include<span>
#include<vector>
#include<cstdint>


template<class T, class Allocator = std::allocator<T>>
class MyContainer
{
public:
    class ConstIterator
    {
    public:
        using iterator_category = std::random_access_iterator_tag;
        using value_type = T;
        using difference_type = size_t;
        using pointer = T* const;
        using reference = const T&;
        
        ConstIterator() = default;
        ConstIterator(const ConstIterator &other) = default;
        
        
        ConstIterator(pointer ptr)
            : m_ptr(ptr) 
        {}

        reference operator*() const
        {
            return *m_ptr;
        }
        pointer operator->()
        {
            return m_ptr;
        }
        // Prefix increment
        ConstIterator& operator++()
        {
            m_ptr++;
            return *this;
        }
        // Postfix increment
        ConstIterator operator++(int)
        {
            Iterator tmp = *this;
            ++(*this);
            return tmp;
        }
        // Prefix decrement
        ConstIterator& operator--()
        {
            m_ptr--;
            return *this;
        }
        // Postfix decrement
        ConstIterator operator--(int)
        {
            Iterator tmp = *this;
            --(*this);
            return tmp;
        }
        ConstIterator& operator+=(const difference_type offset) noexcept
        {
            m_ptr += offset;
            return *this;
        }

        ConstIterator operator+(const difference_type offset) const noexcept
        {
            ConstIterator tmp = *this;
            tmp += offset;
            return tmp;
        }

        ConstIterator& operator-=(const difference_type offset) noexcept
        {
            return *this += -offset;
        }

        ConstIterator operator-(const difference_type offset) const noexcept
        {
            ConstIterator tmp = *this;
            tmp -= offset; 
            return tmp;
        }

        difference_type operator-(const ConstIterator& right) const noexcept
        {
            compatible(right);
            return m_ptr - right.m_ptr;
        }

        reference operator[](const difference_type offset) const noexcept
        {
            return *(*this + offset);
        }
        bool operator==(const ConstIterator& right) const noexcept
        {
            return (*this == right);
        }
        bool operator!=(const ConstIterator& right) const noexcept
        {
            return !(*this == right);
        }

        bool operator<(const ConstIterator& right) const noexcept
        {
            compatible(right);
            return m_ptr < right.m_ptr;
        }

        bool operator>(const ConstIterator& right) const noexcept
        {
            return right < *this;
        }

        bool operator<=(const ConstIterator& right) const noexcept
        {
            return !(right < *this);
        }

        bool operator>=(const ConstIterator& right) const noexcept
        {
            return !(*this < right);
        }
    protected:
        T* m_ptr;
    };
    class Iterator : public ConstIterator
    {
    public:
        using iterator_category = std::random_access_iterator_tag;
        using value_type = T;
        using difference_type = size_t;
        using pointer = T*;
        using reference = T&;
        
        Iterator() = default;
        Iterator(const Iterator &other) = default;

        Iterator(pointer ptr)
            : ConstIterator(ptr)
        {}

        reference operator*() const
        {
            return *ConstIterator::m_ptr;
        }
        pointer operator->()
        {
            return ConstIterator::m_ptr;
        }
        // Prefix increment
        Iterator& operator++()
        {
            ConstIterator::m_ptr++;
            return *this;
        }
        // Postfix increment
        Iterator operator++(int)
        {
            Iterator tmp = *this;
            ++(*this);
            return tmp;
        }
        // Prefix decrement
        Iterator& operator--()
        {
            ConstIterator::m_ptr--;
            return *this;
        }
        // Postfix decrement
        Iterator operator--(int)
        {
            Iterator tmp = *this;
            --(*this);
            return tmp;
        }
        Iterator& operator+=(const difference_type offset) noexcept
        {
            ConstIterator::_Verify_offset(offset);
            ConstIterator::m_ptr += offset;
            return *this;
        }

        Iterator operator+(const difference_type offset) const noexcept
        {
            Iterator tmp = *this;
            tmp += offset;
            return tmp;
        }

        Iterator& operator-=(const difference_type offset) noexcept
        {
            return *this += -offset;
        }

        Iterator operator-(const difference_type offset) const noexcept
        {
            Iterator tmp = *this;
            tmp -= offset;
            return tmp;
        }

        difference_type operator-(const ConstIterator& right) const noexcept
        {
            compatible(right);
            return ConstIterator::m_ptr - right.m_ptr;
        }

        reference operator[](const difference_type offset) const noexcept
        {
            return *(*this + offset);
        }
    };
public:
    using value_type = T;
    using allocator_type = Allocator;
    using pointer = typename std::allocator_traits<Allocator>::pointer;
    using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
    using reference = value_type &;
    using const_reference = const value_type &;
    using size_type = typename std::vector<T>::size_type;
    using difference_type = typename std::vector<T>::difference_type;
    using iterator = Iterator;
    using const_iterator = ConstIterator;
    using reverse_iterator = typename std::reverse_iterator<iterator>;
    using const_reverse_iterator = typename std::reverse_iterator<const_iterator>;

    MyContainer()
    {
        m_data.resize(10);
    }
    iterator begin()
    {
        return iterator(&m_data[0]);
    }
    iterator end()
    {
        return iterator(&m_data[0]+m_data.size());
    }
    const_iterator begin() const
    {
        return const_iterator(&m_data[0]);
    }
    const_iterator end() const
    {
        return const_iterator(&m_data[0]+m_data.size());
    }
   /*
    //These versions of begin() and end() work
    T* begin()
    {
        return &m_data[0];
    }
    T* end()
    {
        return &m_data[0]+m_data.size();
    }
    T* const begin() const
    {
        return &m_data[0];
    }
    T* const end() const
    {
        return &m_data[0]+m_data.size();
    }*/
private:
    std::vector<value_type> m_data;
};

int getSum(std::span<int const>s)
{
    int result =0;
    for (int val : s)
        result += val;
    return result;
}

int main()
{
    MyContainer<int> data;
    int sum = getSum(data);
}

抱歉,这是一个很长的最小示例,但我的理解是自定义迭代器需要提供所有适当的功能才能使用 std::span

我从 clang 得到的编译错误是

<source>:282:12: error: no matching function for call to 'getSum'
        int sum = getSum(data);
                  ^~~~~~
<source>:271:5: note: candidate function not viable: no known conversion from 'MyContainer<int>' to 'std::span<const int>' for 1st argument
int getSum(std::span<int const>s)
    ^
1 error generated.

或来自 VS2019

<source>(282): error C2664: 'int getSum(std::span<const int,18446744073709551615>)': cannot convert argument 1 from 'MyContainer<int,std::allocator<int>>' to 'std::span<const int,18446744073709551615>'
<source>(282): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
<source>(271): note: see declaration of 'getSum'

Clang 最初给我的错误是关于不满足所有各种范围类型的要求,包括输入和输出范围,它指的是 _Begin。但是当我将代码剥离回这个示例时,消息变得不那么详细了。

这让我认为我的迭代器有问题。如果我不返回我的自定义迭代器,而是只返回一个原始指针,那么代码编译得很好。

有什么方法可以找出我的代码不工作的原因吗?我什至都在努力寻找一个合适的指南,让刚接触范围的人能够理解。

【问题讨论】:

  • 我喜欢前缀增量执行后缀增量。
  • 一目了然:(1)你的Iterator::difference_type是无符号的,当a &lt; b和我们a - b时,这意味着废话。 (2) 对于difference_type,您的加法(和减法)运算不是对称的。 (3) 有些方法不是 const 限定的,尽管它们可以而且应该是。 (4) iterator_category 应该是 std::contiguous_iterator_tag 作为跨度要求...之后可能需要完成更多工作,但我只能看到这些。
  • 旁注:你确定要using pointer = T* const;吗?看来using pointer = T const*; 会更合适

标签: c++ iterator range std-span


【解决方案1】:

对于以后再做的任何人,我调试我的迭代器的方式是设置一组静态断言来检查我的迭代器在什么时候没有满足要求。 因为迭代器类型形成了一个层次结构,其中每个迭代器还必须满足层次结构下一层的要求(除了输入和输出相同),我从

开始
    using cType = sci::MyContainer<int>;
    using iType = cType::Iterator;
    static_assert(std::input_iterator<iType>, "failed input iterator");
    static_assert(std::output_iterator<iType, int>, "failed output iterator");
    static_assert(std::forward_iterator<iType>, "failed forward iterator");
    static_assert(std::input_iterator<iType>, "failed input iterator");
    static_assert(std::bidirectional_iterator<iType>, "failed bidirectional iterator");
    static_assert(std::random_access_iterator<iType>, "failed random access iterator");
    static_assert(std::contiguous_iterator<iType>, "failed contiguous iterator");

然后我从第一个错误开始,找到该概念的代码并为子概念设置static_asserts。例如,当我遇到“输入迭代器失败”错误时,我设置了静态断言

    static_assert(std::weakly_incrementable<iType>, "Failed the weakly incrementable test");
    static_assert(std::movable<iType>, "Failed the moveable test");
    static_assert(std::default_initializable<iType>, "Failed the default initializable test");

这让我发现(正如@Unslander Monica 所指出的)我有一个未签名的difference_type,当它需要签名时,我没有Iterator operator+(const difference_type offset, const Iterator &amp;iter) 的运算符,我需要一个element_type 和我的非const 迭代器我需要一个const pointer operator-&gt;() const

【讨论】:

    猜你喜欢
    • 2022-05-23
    • 1970-01-01
    • 2014-08-20
    • 1970-01-01
    • 2011-08-18
    • 2020-12-18
    • 1970-01-01
    • 2021-11-26
    • 2015-07-28
    相关资源
    最近更新 更多