Должен ли каждый причудливый указатель быть итератором?

Я работаю над распределителем памяти на основе сегментов для C ++. В этом распределителе, когда вы освобождаете часть памяти, вы должны знать, какая сегмент оно пришло Поэтому я храню указатель на сегмент как член фантазии 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 может быть реализован как typedef для 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;
^~~~~~~~~~~~~~~~~

Сейчас я Можно исправить это, добавив typedefs «черты итератора» в 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 просто есть ошибка?

(Я уже убедил себя, что самый итераторы не причудливые указатели. Этот вопрос мотивирован моим внезапным сомнением, что, возможно, все модные указатели действительно итераторы.)

2

Решение

Да, вы обязаны выполнять все требования Итератор произвольного доступа. Стандарт C ++ [allocator.requirements] / 5:

Тип распределителя X должен…. X::pointer а также X::const_pointer также должен удовлетворять требованиям для итератора произвольного доступа.

Так что, в частности, вашему причудливому типу указателя требуются пять типов элементов, требуемых для каждого итератор.

Вы также, кажется, пропали без вести fancy_memory_resource<Ptr>::value_typeнесколько необходимых функций, не являющихся членами, и ряд noexcept ключевые слова. Пожалуйста, просмотрите требования к типам распределителей и их типы указателей тщательно.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]