Специализируя std:: опционный
Можно ли будет специализироваться std::optional
на определяемых пользователем типах? Если нет, то не слишком ли поздно предложить это стандарту?
Мой вариант использования для этого-это целочисленный класс, представляющий значение в пределах диапазона. Например, у вас может быть целое число, которое лежит где-то в диапазоне [0, 10]. Многие из моих приложений чувствительны даже к одному байту накладных расходов, поэтому я не смогу использовать неспециализированный std::optional
из-за дополнительного bool
. Однако специализация для std::optional
будет быть тривиальным для целого числа, которое имеет диапазон меньший, чем его базовый тип. Мы могли бы просто сохранить значение 11
в моем примере. Это не должно создавать никаких временных или пространственных накладных расходов на необязательное значение.
Могу ли я создать эту специализацию в namespace std
?
5 ответов:
Общее правило в 17.6.4.2.1 [пространство имен.std] / 1 применяется:
Поэтому я бы сказал, что это разрешено.Программа может добавить специализацию шаблона для любого шаблона стандартной библиотеки в пространство имен
std
только в том случае, если объявление зависит от определяемого пользователем типа и специализация соответствует требованиям стандартной библиотеки для исходного шаблона и не является явно выраженной. запрещенный.N. B.
optional
не будет частью стандарта C++14, он будет включен в отдельная техническая спецификация на библиотечные основы, так что есть время изменить правило, если моя интерпретация неверна.
Если вы ищете библиотеку, которая эффективно упаковывает значение и флаг "no-value" в одно место памяти, я рекомендую посмотреть на compact_optional. Он делает именно это.
Он не специализируется на
boost::optional
илиstd::experimental::optional
, но он может обернуть их внутрь, давая вам единообразный интерфейс, с оптимизациями, где это возможно, и резервным вариантом "классического" опционально, где это необходимо.
Я спрашивал о том же самом, относительно специализации
optional<bool>
иoptional<tribool>
среди других примеров, чтобы использовать только один байт. В то время как" законность " таких вещей не обсуждалась, я действительно думаю, что теоретически не следует позволять специализироватьсяoptional<T>
в отличие от eg.: хэш (который явно разрешен).У меня нет журналов, но часть обоснования заключается в том, что интерфейс обрабатывает доступ к данным как доступ к указателю или ссылке, что означает, что если вы используйте другую структуру данных во внутренних системах, некоторые инварианты доступа могут измениться; не говоря уже о предоставлении интерфейсу доступа к данным может потребоваться что-то вроде
reinterpret_cast<(some_reference_type)>
. Использованиеuint8_t
для хранения необязательного-bool, например, наложило бы несколько дополнительных требований на интерфейсoptional<bool>
, которые отличаются от требованийoptional<T>
. Каким должен быть, например, возвращаемый типoperator*
?В основном, я предполагаю, что идея состоит в том, чтобы избежать целого
vector<bool>
опять фиаско.В вашем примере это может быть не так уж плохо, поскольку тип доступа по-прежнему
your_integer_type&
(или указатель). Но в этом случае простое проектирование вашего целочисленного типа с учетом "зомби" или "неопределенного" значения вместо того, чтобы полагаться наoptional<>
, чтобы сделать работу за вас, с его дополнительными накладными расходами и требованиями, может быть самым безопасным выбором.
Я не вижу, как разрешить или не разрешить какой-то определенный битовый шаблон для представления ненагруженного состояния подпадает под что-либо стандартное покрытие.
Если бы вы пытались убедить поставщика библиотеки сделать это, это потребовало бы реализации, исчерпывающих тестов, чтобы показать, что вы случайно не нарушили ни одно из требований необязательного (или случайно вызванного неопределенного поведения) и обширного бенчмаркинга, чтобы показать, что это имеет заметное значение в реальном мире (а не только в реальном мире). надуманные) ситуации.
Конечно, вы можете делать все, что хотите, со своим собственным кодом.
Облегчите выбор в пользу экономии места
Я решил, что это полезная вещь, но полная специализация-это немного больше работы, чем необходимо (например, получениеoperator=
правильного).Я разместил в списке рассылки Boost способ упростить задачу специализации, особенно если вы хотите специализировать только некоторые экземпляры класса шаблон.
Http://boost.2283326.n4.nabble.com/optional-Specializing-optional-to-save-space-td4680362.html
Мой текущий интерфейс включает в себя специальный тип тега, используемый для "разблокировки" доступа к определенным функциям. Я творчески назвал этот тип
optional_tag
. Толькоoptional
может построитьoptional_tag
. Для того чтобы тип согласился на пространственно-эффективное представление, ему необходимы следующие функции-члены:
T(optional_tag)
создает неинициализированное значениеinitialize(optional_tag, Args && ...)
конструирует объект, когда он уже может существоватьuninitialize(optional_tag)
разрушает содержащийся в нем объектis_initialized(optional_tag)
проверяет, находится ли объект в настоящее время в инициализированном состоянииВсегда требуя параметр optional_tag, мы не ограничиваем сигнатуры функций. Вот почему, например, мы не можем использовать
operator bool()
в качестве теста, потому что тип может хотеть этот оператор по другим причинам.Преимущество этого перед некоторыми другими возможными методами реализация его заключается в том, что вы можете заставить его работать с любым типом, который может естественным образом поддерживать такое состояние. Он не добавляет никаких требований, таких как наличие конструктора перемещения.
Вы можете увидеть полную реализацию идеи в коде на странице
И для класса, использующего специализация:
(строки 220-242)
Альтернативный подход
Это в отличие от моей предыдущей реализации, которая требовала от пользователей специализировать шаблон класса. Вы можете увидеть старую версию здесь:
И
Проблема с этим подходом заключается в том, что это просто больше работы для пользователя. Вместо добавления четырех функций-членов, пользователь необходимо перейти в новое пространство имен и специализировать шаблон. На практике все специализации будут иметь конструкторin_place_t
, который перенаправляет все аргументы в базовый тип. Подходoptional_tag
, с другой стороны, может просто использовать конструкторы базового типа напрямую.В подходе specialize
optional_storage
пользователь также несет ответственность за добавление соответствующих ссылочных перегрузок функции value. В подходеoptional_tag
мы уже имеем значение, поэтому мы не делаем придется его вытаскивать.
optional_storage
также требуется стандартизация в составе интерфейса опционально двух вспомогательных классов, только на одном из которых пользователь должен специализироваться (а иногда и делегировать свою специализацию другому).Разница между этим и compact_optional
compact_optional
это способ сказать: "рассматривайте это специальное значение sentinel как тип, которого нет, почти как NaN". Он требует, чтобы пользователь знал, что тип, с которым он работает, имеет какой-то особенный страж. Легко специализируемыйoptional
- это способ сказать: "мой тип не нуждается в дополнительном пространстве для хранения состояния not present, но это состояние не является нормальным значением."Это не требует, чтобы кто-то знал об оптимизации, чтобы воспользоваться им; каждый, кто использует тип, получает его бесплатно.Будущее
Моя цель - сделать это сначала в boost:: optional, а затем частью предложения std::optional. До тех пор вы всегда можете использовать
bounded::optional
, хотя он имеет несколько других (преднамеренных) различий интерфейса.