【发布时间】: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 < 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