Специализируя std:: опционный


Можно ли будет специализироваться std::optional на определяемых пользователем типах? Если нет, то не слишком ли поздно предложить это стандарту?

Мой вариант использования для этого-это целочисленный класс, представляющий значение в пределах диапазона. Например, у вас может быть целое число, которое лежит где-то в диапазоне [0, 10]. Многие из моих приложений чувствительны даже к одному байту накладных расходов, поэтому я не смогу использовать неспециализированный std::optional из-за дополнительного bool. Однако специализация для std::optional будет быть тривиальным для целого числа, которое имеет диапазон меньший, чем его базовый тип. Мы могли бы просто сохранить значение 11 в моем примере. Это не должно создавать никаких временных или пространственных накладных расходов на необязательное значение.

Могу ли я создать эту специализацию в namespace std?

5 8

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() в качестве теста, потому что тип может хотеть этот оператор по другим причинам.

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

Вы можете увидеть полную реализацию идеи в коде на странице

Https://bitbucket.org/davidstone/bounded_integer/src/8c5e7567f0d8b3a04cc98142060a020b58b2a00f/bounded_integer/detail/optional/optional.hpp?at=default&fileviewer=file-view-default

И для класса, использующего специализация:

Https://bitbucket.org/davidstone/bounded_integer/src/8c5e7567f0d8b3a04cc98142060a020b58b2a00f/bounded_integer/detail/class.hpp?at=default&fileviewer=file-view-default

(строки 220-242)

Альтернативный подход

Это в отличие от моей предыдущей реализации, которая требовала от пользователей специализировать шаблон класса. Вы можете увидеть старую версию здесь:

Https://bitbucket.org/davidstone/bounded_integer/src/2defec41add2079ba023c2c6d118ed8a274423c8/bounded_integer/detail/optional/optional.hpp

И

Https://bitbucket.org/davidstone/bounded_integer/src/2defec41add2079ba023c2c6d118ed8a274423c8/bounded_integer/detail/optional/specialization.hpp

Проблема с этим подходом заключается в том, что это просто больше работы для пользователя. Вместо добавления четырех функций-членов, пользователь необходимо перейти в новое пространство имен и специализировать шаблон. На практике все специализации будут иметь конструктор 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, хотя он имеет несколько других (преднамеренных) различий интерфейса.