Я работаю над распределителем памяти на основе сегментов для 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
просто есть ошибка?
(Я уже убедил себя, что самый итераторы не причудливые указатели. Этот вопрос мотивирован моим внезапным сомнением, что, возможно, все модные указатели действительно итераторы.)
Да, вы обязаны выполнять все требования Итератор произвольного доступа. Стандарт C ++ [allocator.requirements] / 5:
Тип распределителя
X
должен….X::pointer
а такжеX::const_pointer
также должен удовлетворять требованиям для итератора произвольного доступа.
Так что, в частности, вашему причудливому типу указателя требуются пять типов элементов, требуемых для каждого итератор.
Вы также, кажется, пропали без вести fancy_memory_resource<Ptr>::value_type
несколько необходимых функций, не являющихся членами, и ряд noexcept
ключевые слова. Пожалуйста, просмотрите требования к типам распределителей и их типы указателей тщательно.
Других решений пока нет …