Почему не std:: algorithms constexpr и что может быть?
Почему нет методов std::algorithm
constexpr
? Если я правильно понимаю новые правила С++ 14, многие из этих методов могут быть constexpr
. Например, почему не может std::find
быть constexpr
?
static constexpr std::array<char, 4> DnaBases {'A', 'C', 'G', 'T'};
constexpr bool is_dna(char b)
{
return std::find(std::cbegin(DnaBases), std::cend(DnaBases), b) != std::cend(DnaBases); // why not?
}
Какой другой std::algorithm
может быть constexpr
?
Ответы
Ответ 1
Это может быть constexpr
, но не может быть оценено как константное выражение, так как в этом случае, например, для поиска во время компиляции требуется, чтобы:
begin/end
должен быть constexpr, * operator
итератора должен быть constexpr, operator ==
должен быть constexpr, operator !=
, поскольку итератор должен быть constexpr, operator ++
, поскольку итератор должен быть constexpr. Но если все функции constexpr
, то многие алгоритмы могут быть реализованы с помощью constexpr
.
Вы можете посмотреть библиотеку SPROUT для реализации контейнеров/алгоритмов constexpr.
И связанный разговор на форумах isocpp.org
Ответ 2
Функции не могут быть перегружены на основе constexpr
-ness. В результате любая функция, определенная как constexpr
, должна быть реализована в форме, которая может быть constexpr
. Это требование накладывает ограничения на все реализации.
Спецификация С++ 14 несколько смягчена относительно ограничений по сравнению с С++ 11. Однако, когда спецификация была завершена, никто не был уверен, что все оптимизации, которые могут быть достигнуты без ограничения constexpr
, могут быть достигнуты, когда алгоритмы должны быть constexpr
. Не зная, что функциональность не constexpr
не мешает выполнению реализаций constexpr
, алгоритмы не будут определены как constexpr
. Использование алгоритмов не constexpr
по-прежнему считается основным использованием алгоритмов.
Возможно, стоит иметь специальный набор алгоритмов, которые определены как constexpr
. Я не знаю подходящего предложения. Я также не вижу многого, если спрос требует стандартизации, но мое восприятие может отличаться от других.
Ответ 3
Текущая (С++ 14) стандартная библиотека в значительной степени недоступна w.r.t. к соответствующим возможностям основного языка в отношении constexpr
.
Например, MSVC 2015, который поддерживает только язык С++ 11 для constexpr
, мог почти полностью реализовать использование стандартной библиотеки С++ 14 constexpr
. Единственными исключениями были std::min
, std::max
, std::minmax
, std::min_element
, std::max_element
, std::minmax_element
для std::initializer_list
.
С С++ 1z (17?) алгоритмы std::xxx_element
станут constexpr
алгоритмами для общего итератора и входов компаратора, чтобы унифицировать использование для std::initializer_list
. Кроме того, существуют предложения для constexpr
lambda для С++ 1z.
При обновлении lambdas все еще осталось несколько ограничений для основного языка, чтобы предотвратить заголовок <algorithm>
всего constexpr
. (Имейте в виду, что это не технологические препятствия, а большинство из них могут быть разрешены, позволяя компилятору оценить их).
- Некоторые алгоритмы могут динамически распределять память, вызывая
std::get_temporary_buffer
(std::inplace_merge
, std::stable_sort
и std::stable_partition
), что недопустимо в контекстах constexpr
.
- Некоторые алгоритмы могут вернуться к низкоуровневым подпрограммам C, таким как
memset
(std::fill
и std::fill_n
), что помешало бы авторам библиотек использовать эти алгоритмы в контекстах constexpr
.
- Некоторые реализации алгоритмов могут извлечь выгоду из разумного использования
goto
(например, std::nth_element
, std::stable_sort
), для которого C++1z
предложение было отклонено.
- И последнее, но не менее важное:
constexpr
- это изменение интерфейса, обещая, что все будущие реализации должны будут выполнить это обещание. По этой причине реализациям не разрешается добавлять constexpr
в качестве функции качества выполнения (в отличие от noexcept
).
В частности, четвертый вопрос останавливает эксперименты с тем, насколько constexpr
может быть нажата для стандартной библиотеки (алгоритмы, контейнеры и другие утилиты). Вместо этого отдельное предложение должно быть написано и одобрено для каждого расширения constexpr
.
Ответ 4
std::algorithm
алгоритмы действуют на итераторы. Существует техническая причина, по которой их constexpr
обычно либо предотвращает их компиляцию (в С++ 11), либо ничего не делает (в С++ 14 или с условным constexpr
), но также есть семантическая причина, по которой это не имеет смысла для них быть constexpr
.
Техническая причина заключается в том, что функции constexpr
не могут вызывать выражения не constexpr
. ForEveR указывает, что функции шаблона constexpr
не могут быть вычислены во время компиляции, если они вызывают выражения не constexpr
.
В случае std::algorithm
оценка функций constexpr
в std::algorithm
потребует, чтобы функции доступа к итераторам контейнеров были constexpr
, что в свою очередь потребовало бы, чтобы итераторы были типами constexpr
. Но это невозможно почти по определению; контейнеры обычно проектируются как облегченный доступ к памяти, выделенной кучей, но память кучи не может быть выделена во время компиляции (конечно). В приведенных ниже комментариях dyp указывает, что итераторы не всегда указывают на контейнеры, но даже эти итераторы вряд ли будут использоваться во время компиляции; например, объекты потоков, конечно, не читаются или записываются во время компиляции, так как IO не может быть выполнено во время компиляции.
Это приводит к семантической проблеме: constexpr
семантически означает, что функция должна быть оценена во время компиляции. Объявление условных функций constexpr
, когда невозможно оценить их во время компиляции, сделает API запутывающим и вводящим в заблуждение.
Теперь, , я думаю, что язык был бы улучшен, если бы существовал способ создания и использования контейнеров во время компиляции; это сделало бы constexpr
более похожим на возможности макроса Lisp. В конечном итоге это может быть добавлено, но в настоящее время оно не поддерживается существующим стандартным библиотечным кодом. Самый гибкий подход, позволяющий некоторым объектам жить в куче во время компиляции, как уже упоминалось выше, вообще не поддерживается основным языком, и это создаст серьезные осложнения. Например, что было бы законным делать с такими объектами? Либо их продолжительность жизни должна быть ограничена только временем компиляции, либо они должны быть включены как статическая память const в финальной программе (например, строковый литерал) или... что?
Ответ 5
Это тема предложения Антония Полухина P0202:
Добавить модификаторы Constexpr для функций и заголовков
который, я надеюсь, будет принят С++ Рабочая группа по эволюции библиотек и пробивается на С++ 20.