Ответ 1
Чтобы решить эту проблему, я создал функцию to_raw_pointer
, которая работает для любого "причудливого указателя", который реализует operator->()
. Вы можете найти его в libС++.
Вот он:
template <class _Tp>
inline _LIBCPP_INLINE_VISIBILITY
_Tp*
__to_raw_pointer(_Tp* __p) _NOEXCEPT
{
return __p;
}
template <class _Pointer>
inline _LIBCPP_INLINE_VISIBILITY
typename pointer_traits<_Pointer>::element_type*
__to_raw_pointer(_Pointer __p) _NOEXCEPT
{
return _VSTD::__to_raw_pointer(__p.operator->());
}
Он работает, вызывая operator->()
нетрадиционным способом. Этот оператор должен либо вызвать другой operator->()
, либо вернуть реальный указатель. Перегрузка для реальных указателей ломает рекурсию с помощью функции идентификации. Таким образом, это будет использоваться как:
this->_Getal().construct(__to_raw_pointer(this->_Mylast),
_STD forward<value_type>(this->_Myfirst[_Idx]));
construct
указан, чтобы взять реальный указатель, а не причудливый указатель. И нет никакого неявного преобразования, указанного от причудливых указателей до реальных указателей. Контейнер должен использовать что-то вроде to_raw_pointer
или addressof
.
Контейнер также должен вызывать construct
через allocator_traits
вместо того, чтобы называть его непосредственно на сохраненном распределителе, как показано. Это означает, что construct
"по умолчанию" используется allocator_traits
, а не требует, чтобы распределитель реализовал construct
.
В настоящее время как operator*()
, так и operator->()
обычно требуется, чтобы причудливый указатель был непустым до вызова этого оператора на нем. Однако я ожидаю, что это требование будет смягчено для operator->()
в будущем.
Обновление
Я очень торопился, когда писал выше. Теперь, когда у меня есть время, я собираюсь включить полные требования к типам allocator::pointer
. Однако при повторном чтении вопроса я вижу, что Maxym уже хорошо справился с этим в вопросе, поэтому я не буду повторять их здесь.
Единственное, что находится в std, но не полностью очевидно, - это неявные и явные преобразования между четырьмя типами указателей: pointer
, const_pointer
, void_pointer
и const_void_pointer
:
implicit allocator pointer conversions:
+--------------------------------------+
| pointer --> const_pointer |
| | \ | |
| | --------- | |
| \|/ _\| \|/ |
| void_pointer --> const_void_pointer |
+--------------------------------------+
explicit allocator pointer conversions:
+--------------------------------------+
| pointer const_pointer |
| /|\ /|\ |
| | | |
| | | |
| void_pointer const_void_pointer |
+--------------------------------------+
То есть вы можете неявно конвертировать из не const
в const
, а от не void
в void
, и вы можете явно преобразовать из void
в non void
. Но нет контейнера для const_cast
(отбрасывание const
-ness) из allocator::const_pointer
или allocator::const_void_pointer
. Как только контейнер отправится const
, он никогда не сможет вернуться.