c++ - 每个花哨的指针都应该是一个迭代器吗?

标签 c++ crtp allocator fancy-pointers

我正在为 C++ 开发基于段的内存分配器。在这个分配器中,当你释放一 block 内存时,你必须知道它来自哪个。因此,我将指向该段的指针存储为花式 pointer 的成员。从分配器的 allocate 返回功能。

只是为了显示我所说的界面:这里是 fancy_memory_resource支持我的分配器...

template<class Ptr>
class fancy_memory_resource {
public:
    Ptr allocate(size_t bytes, size_t align = alignof(max_align_t)) {
        return do_allocate(bytes, align);
    }
    void deallocate(Ptr p, size_t bytes, size_t align = alignof(max_align_t)) {
        return do_deallocate(p, bytes, align);
    }
    bool is_equal(const fancy_memory_resource& rhs) const noexcept {
        return do_is_equal(rhs);
    }
    virtual ~fancy_memory_resource() = default;
private:
    virtual Ptr do_allocate(size_t bytes, size_t align) = 0;
    virtual void do_deallocate(Ptr p, size_t bytes, size_t align) = 0;
    virtual bool do_is_equal(const fancy_memory_resource& rhs) const noexcept = 0;
};

(注意 std::pmr::memory_resource 可以实现为 fancy_memory_resource<void*> 的类型定义。这是我故意的。)

与此同时,Ptr有问题的是一个名为 segmented_fancy_pointer<T> 的奇特指针类型(未图示)继承自 CRTP 类型 fancy_ptr_base<T, segmented_fancy_pointer<T>> ...

template<class T, class CRTP>
struct fancy_ptr_base {
    constexpr T *ptr() const noexcept { return m_ptr; }
    constexpr explicit operator T*() const noexcept { return ptr(); }
    constexpr explicit operator bool() const noexcept { return ptr() != nullptr; }
    constexpr bool operator==(CRTP b) const { return ptr() == b.ptr(); }
    constexpr bool operator!=(CRTP b) const { return ptr() != b.ptr(); }
    constexpr bool operator==(decltype(nullptr)) const { return ptr() == nullptr; }
    constexpr bool operator!=(decltype(nullptr)) const { return ptr() != nullptr; }
    constexpr bool operator<(CRTP b) const { return ptr() < b.ptr(); }
    constexpr bool operator<=(CRTP b) const { return ptr() <= b.ptr(); }
    constexpr bool operator>(CRTP b) const { return ptr() > b.ptr(); }
    constexpr bool operator>=(CRTP b) const { return ptr() >= b.ptr(); }
    constexpr T& operator*() const noexcept { return *ptr(); }
    constexpr T* operator->() const noexcept { return ptr(); }
    constexpr CRTP& operator+=(ptrdiff_t i) { m_ptr += i; return as_crtp(); }
    constexpr CRTP& operator-=(ptrdiff_t i) { m_ptr -= i; return as_crtp(); }
    constexpr CRTP& operator++() { ++m_ptr; return as_crtp(); }
    constexpr CRTP& operator--() { --m_ptr; return as_crtp(); }
    constexpr CRTP operator++(int) { auto r(as_crtp()); ++*this; return r; }
    constexpr CRTP operator--(int) { auto r(as_crtp()); --*this; return r; }
    constexpr CRTP operator+(ptrdiff_t i) const { auto r(as_crtp()); r += i; return r; }
    constexpr CRTP operator-(ptrdiff_t i) const { auto r(as_crtp()); r -= i; return r; }
    constexpr ptrdiff_t operator-(CRTP b) const { return ptr() - b.ptr(); }

protected:
    T *m_ptr = nullptr;
private:
    constexpr CRTP& as_crtp() { return *static_cast<CRTP*>(this); }
    constexpr const CRTP& as_crtp() const { return *static_cast<const CRTP*>(this); }
};

template<class CRTP>
struct fancy_ptr_base<void, CRTP> {
    constexpr void *ptr() const noexcept { return m_ptr; }
    constexpr explicit operator void*() const noexcept { return ptr(); }
    constexpr explicit operator bool() const noexcept { return ptr() != nullptr; }
    constexpr bool operator==(CRTP b) const { return ptr() == b.ptr(); }
    constexpr bool operator!=(CRTP b) const { return ptr() != b.ptr(); }
    constexpr bool operator==(decltype(nullptr)) const { return ptr() == nullptr; }
    constexpr bool operator!=(decltype(nullptr)) const { return ptr() != nullptr; }
    constexpr bool operator<(CRTP b) const { return ptr() < b.ptr(); }
    constexpr bool operator<=(CRTP b) const { return ptr() <= b.ptr(); }
    constexpr bool operator>(CRTP b) const { return ptr() > b.ptr(); }
    constexpr bool operator>=(CRTP b) const { return ptr() >= b.ptr(); }
protected:
    void *m_ptr = nullptr;
};

现在是真正的问题。当我去使用我的 segmented_allocator<T> (未显示)与 libc++ 的 std::vector ,一切正常。当我尝试将它与 libstdc++ 的 std::vector 一起使用时, 它失败了:

In file included from /opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_algobase.h:67:0,
                 from /opt/wandbox/gcc-head/include/c++/8.0.0/vector:60,
                 from prog.cc:1984:
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_iterator.h: In instantiation of 'class __gnu_cxx::__normal_iterator<scratch::segmented_fancy_pointer<int>, std::vector<int, scratch::pmr::propagating_polymorphic_allocator<int, scratch::segmented_fancy_pointer<int> > > >':
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/vector.tcc:105:25:   required from 'std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int}; _Tp = int; _Alloc = scratch::pmr::propagating_polymorphic_allocator<int, scratch::segmented_fancy_pointer<int> >; std::vector<_Tp, _Alloc>::reference = int&]'
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_vector.h:954:21:   required from 'void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = int; _Alloc = scratch::pmr::propagating_polymorphic_allocator<int, scratch::segmented_fancy_pointer<int> >; std::vector<_Tp, _Alloc>::value_type = int]'
prog.cc:1990:18:   required from here
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_iterator.h:770:57: error: no type named 'iterator_category' in 'struct std::iterator_traits<scratch::segmented_fancy_pointer<int> >'
       typedef typename __traits_type::iterator_category iterator_category;
                                                         ^~~~~~~~~~~~~~~~~

现在,我可以通过将“迭代器特征”typedef 添加到 fancy_ptr_base<T, CRTP> 来解决这个问题,像这样:

    using pointer = CRTP;
    using reference = T&;
    using value_type = std::remove_cv_t<T>;
    using iterator_category = std::random_access_iterator_tag;
    using difference_type = ptrdiff_t;

但是我必须这样做吗?是否要求每个花哨的指针类型也都是迭代器类型?或者 libc++ 是否在做正确的事情而 libstdc++ 的 vector只是有一个错误?

(我已经说服自己 most iterators are not fancy pointers。这个问题是由于我突然怀疑也许所有花哨的指针确实是迭代器。)

最佳答案

是的,您需要执行 Random Access Iterator 的所有要求. C++ 标准 [allocator.requirements]/5:

An allocator type X shall.... X::pointer and X::const_pointer shall also satisfy the requirements for a random access iterator.

特别是,您的奇特指针类型需要每个 iterator 所需的五个成员类型.

你好像也不见了fancy_memory_resource<Ptr>::value_type ,几个需要的非成员函数,以及一些 noexcept关键字。请查看requirements for allocator types以及它们的指针类型。

关于c++ - 每个花哨的指针都应该是一个迭代器吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45132546/

相关文章:

c++ - "excess elements in scalar initializer"使用 igraph C 库生成具有幂律度分布的网络时

c++ - 无法从子类访问 QMainWindow 变量

c++ - 基于 CRTP 的解决方案会是什么样子?

c++ - 使用 CRTP 的模板中的继承类型

c++ - CRTP 初始化列表构造函数错误

c++ - 预分配列表

c++ - 如何跟踪列表列表的分配内存?

c++ - 以我的方式向后写一个字符串,有时会导致额外的输出,与字符 ' w ' 相关

c++ - Qt 应用程序部署 - 不运行,但没有错误消息

c++ - boost aligned_allocator 对齐参数不影响实际对齐